From 932f1c97372bddd99944ebadbff51e5e19ed587f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 May 2019 15:17:36 +0100 Subject: [PATCH 001/210] Implement TransparentAddress encoding and decoding --- zcash_client_backend/Cargo.toml | 1 + zcash_client_backend/src/constants/mainnet.rs | 10 ++ zcash_client_backend/src/constants/testnet.rs | 10 ++ zcash_client_backend/src/encoding.rs | 105 ++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 562d1a50e5..b7dd7b7315 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] bech32 = "0.7" +bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } hex = "0.3" pairing = { version = "0.16", path = "../pairing" } diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_client_backend/src/constants/mainnet.rs index f3f020dc58..db932fa568 100644 --- a/zcash_client_backend/src/constants/mainnet.rs +++ b/zcash_client_backend/src/constants/mainnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs"; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xb8]; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xbd]; diff --git a/zcash_client_backend/src/constants/testnet.rs b/zcash_client_backend/src/constants/testnet.rs index 5379d5abd9..d39a11d535 100644 --- a/zcash_client_backend/src/constants/testnet.rs +++ b/zcash_client_backend/src/constants/testnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling"; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25]; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba]; diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index ba999126c6..91522fd505 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -6,9 +6,12 @@ //! [`constants`]: crate::constants use bech32::{self, Error, FromBase32, ToBase32}; +use bs58::{self, decode::Error as Bs58Error}; use pairing::bls12_381::Bls12; +use std::convert::TryInto; use std::io::{self, Write}; use zcash_primitives::{ + legacy::TransparentAddress, primitives::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, @@ -176,6 +179,108 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result String { + let decoded = match addr { + TransparentAddress::PublicKey(key_id) => { + let mut decoded = vec![0; pubkey_version.len() + 20]; + decoded[..pubkey_version.len()].copy_from_slice(pubkey_version); + decoded[pubkey_version.len()..].copy_from_slice(key_id); + decoded + } + TransparentAddress::Script(script_id) => { + let mut decoded = vec![0; script_version.len() + 20]; + decoded[..script_version.len()].copy_from_slice(script_version); + decoded[script_version.len()..].copy_from_slice(script_id); + decoded + } + }; + bs58::encode(decoded).with_check().into_string() +} + +/// Decodes a [`TransparentAddress`] from a Base58Check-encoded string. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX}, +/// encoding::decode_transparent_address, +/// }; +/// use zcash_primitives::legacy::TransparentAddress; +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", +/// ), +/// Ok(Some(TransparentAddress::PublicKey([0; 20]))), +/// ); +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", +/// ), +/// Ok(Some(TransparentAddress::Script([0; 20]))), +/// ); +/// ``` +pub fn decode_transparent_address( + pubkey_version: &[u8], + script_version: &[u8], + s: &str, +) -> Result, Bs58Error> { + bs58::decode(s).with_check(None).into_vec().map(|decoded| { + if decoded.starts_with(pubkey_version) { + decoded[pubkey_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::PublicKey) + } else if decoded.starts_with(script_version) { + decoded[script_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::Script) + } else { + None + } + }) +} + #[cfg(test)] mod tests { use pairing::bls12_381::Bls12; From 6e53cf3c4cd818d0502a4024370665b5ac5cb9f9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 26 Mar 2020 19:00:46 +1300 Subject: [PATCH 002/210] group: Take scalar by reference in CurveProjective::recommended_wnaf_for_scalar --- bellman/src/groth16/tests/dummy_engine.rs | 2 +- group/src/lib.rs | 2 +- group/src/wnaf.rs | 2 +- pairing/src/bls12_381/ec.rs | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 6c011c3ea0..21322d86a9 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -423,7 +423,7 @@ impl CurveProjective for Fr { *self } - fn recommended_wnaf_for_scalar(_: ::Repr) -> usize { + fn recommended_wnaf_for_scalar(_: &::Repr) -> usize { 3 } diff --git a/group/src/lib.rs b/group/src/lib.rs index 34c8ac208b..3dd9bbd217 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -82,7 +82,7 @@ pub trait CurveProjective: /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize; + fn recommended_wnaf_for_scalar(scalar: &::Repr) -> usize; /// Recommends a wNAF window size given the number of scalars you intend to multiply /// a base by. Always returns a number between 2 and 22, inclusive. diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 381cd106b8..70f880b8a7 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -115,7 +115,7 @@ impl Wnaf<(), Vec, Vec> { scalar: <::Scalar as PrimeField>::Repr, ) -> Wnaf, &[i64]> { // Compute the appropriate window size for the scalar. - let window_size = G::recommended_wnaf_for_scalar(scalar); + let window_size = G::recommended_wnaf_for_scalar(&scalar); // Compute the wNAF form of the scalar. wnaf_form(&mut self.scalar, scalar, window_size); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index a87fe11f84..2dae1ea0e8 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -674,7 +674,7 @@ macro_rules! curve_impl { (*self).into() } - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { + fn recommended_wnaf_for_scalar(scalar: &::Repr) -> usize { Self::empirical_recommended_wnaf_for_scalar(scalar) } @@ -1014,7 +1014,7 @@ pub mod g1 { } impl G1 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + fn empirical_recommended_wnaf_for_scalar(scalar: &FrRepr) -> usize { let num_bits = scalar.num_bits() as usize; if num_bits >= 130 { @@ -1733,7 +1733,7 @@ pub mod g2 { } impl G2 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + fn empirical_recommended_wnaf_for_scalar(scalar: &FrRepr) -> usize { let num_bits = scalar.num_bits() as usize; if num_bits >= 103 { From 69c60530d41fc17c2ea52a42135c7b90901c6182 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 26 Mar 2020 19:23:29 +1300 Subject: [PATCH 003/210] group: Rewrite wNAF to remove dependency on ff::PrimeFieldRepr Adapted from Scalar::non_adjacent_form in curve25519-dalek. --- bellman/src/groth16/generator.rs | 14 +++--- group/Cargo.toml | 1 + group/src/tests/mod.rs | 30 ++++++------ group/src/wnaf.rs | 81 ++++++++++++++++++++++---------- 4 files changed, 78 insertions(+), 48 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index d99383555e..02efc21bd6 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -2,7 +2,7 @@ use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::{Field, PrimeField}; +use ff::Field; use group::{CurveAffine, CurveProjective, Wnaf}; use pairing::Engine; @@ -273,7 +273,7 @@ where exp.mul_assign(&coeff); // Exponentiate - *h = g1_wnaf.scalar(exp.into_repr()); + *h = g1_wnaf.scalar(&exp); } // Batch normalize @@ -376,14 +376,14 @@ where // Compute A query (in G1) if !at.is_zero() { - *a = g1_wnaf.scalar(at.into_repr()); + *a = g1_wnaf.scalar(&at); } // Compute B query (in G1/G2) if !bt.is_zero() { - let bt_repr = bt.into_repr(); - *b_g1 = g1_wnaf.scalar(bt_repr); - *b_g2 = g2_wnaf.scalar(bt_repr); + (); + *b_g1 = g1_wnaf.scalar(&bt); + *b_g2 = g2_wnaf.scalar(&bt); } at.mul_assign(&beta); @@ -394,7 +394,7 @@ where e.add_assign(&ct); e.mul_assign(inv); - *ext = g1_wnaf.scalar(e.into_repr()); + *ext = g1_wnaf.scalar(&e); } // Batch normalize diff --git a/group/Cargo.toml b/group/Cargo.toml index 9b8fe5f766..b68c2fe6b9 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -15,6 +15,7 @@ repository = "https://github.com/ebfull/group" edition = "2018" [dependencies] +byteorder = { version = "1", default-features = false } ff = { version = "0.6", path = "../ff" } rand = "0.7" rand_xorshift = "0.2" diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index c53ba762f7..66a76c0965 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -85,12 +85,12 @@ fn random_wnaf_tests() { for w in 2..14 { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); wnaf_table(&mut table, g, w); - wnaf_form(&mut wnaf, s, w); + wnaf_form(&mut wnaf, s.into_repr(), w); let g2 = wnaf_exp(&table, &wnaf); assert_eq!(g1, g2); @@ -103,17 +103,17 @@ fn random_wnaf_tests() { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); let g2 = { let mut wnaf = Wnaf::new(); - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g3 = { let mut wnaf = Wnaf::new(); - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g4 = { let mut wnaf = Wnaf::new(); @@ -121,11 +121,11 @@ fn random_wnaf_tests() { only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g5 = { let mut wnaf = Wnaf::new(); - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); @@ -137,40 +137,40 @@ fn random_wnaf_tests() { { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g7 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g8 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } let mut shared = wnaf.base(g, 1).shared(); only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g9 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 70f880b8a7..200110157a 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -1,4 +1,5 @@ -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use std::iter; use super::CurveProjective; @@ -16,31 +17,60 @@ pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, wi } } -/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. -pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { +/// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian +/// scalar. +pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { + // Required by the NAF definition + debug_assert!(window >= 2); + // Required so that the NAF digits fit in i64 + debug_assert!(window <= 64); + wnaf.truncate(0); - while !c.is_zero() { - let mut u; - if c.is_odd() { - u = (c.as_ref()[0] % (1 << (window + 1))) as i64; + let u64_len = c.as_ref().len(); + let bit_len = u64_len * 64; - if u > (1 << window) { - u -= 1 << (window + 1); - } + let mut c_u64 = vec![0u64; u64_len + 1]; + c_u64[0..u64_len].copy_from_slice(c.as_ref()); - if u > 0 { - c.sub_noborrow(&S::from(u as u64)); - } else { - c.add_nocarry(&S::from((-u) as u64)); - } + let width = 1u64 << window; + let window_mask = width - 1; + + let mut pos = 0; + let mut carry = 0; + while pos < bit_len { + // Construct a buffer of bits of the scalar, starting at bit `pos` + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let bit_buf = if bit_idx + window < 64 { + // This window's bits are contained in a single u64 + c_u64[u64_idx] >> bit_idx + } else { + // Combine the current u64's bits with the bits from the next u64 + (c_u64[u64_idx] >> bit_idx) | (c_u64[u64_idx + 1] << (64 - bit_idx)) + }; + + // Add the carry into the current window + let window_val = carry + (bit_buf & window_mask); + + if window_val & 1 == 0 { + // If the window value is even, preserve the carry and emit 0. + // Why is the carry preserved? + // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 + // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 + wnaf.push(0); + pos += 1; } else { - u = 0; + wnaf.push(if window_val < width / 2 { + carry = 0; + window_val as i64 + } else { + carry = 1; + (window_val as i64).wrapping_sub(width as i64) + }); + wnaf.extend(iter::repeat(0).take(window - 1)); + pos += window; } - - wnaf.push(u); - - c.div2(); } } @@ -112,8 +142,10 @@ impl Wnaf<(), Vec, Vec> { /// exponentiations with `.base(..)`. pub fn scalar( &mut self, - scalar: <::Scalar as PrimeField>::Repr, + scalar: &::Scalar, ) -> Wnaf, &[i64]> { + let scalar = scalar.into_repr(); + // Compute the appropriate window size for the scalar. let window_size = G::recommended_wnaf_for_scalar(&scalar); @@ -168,14 +200,11 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar( - &mut self, - scalar: <::Scalar as PrimeField>::Repr, - ) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.into_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } } From b6457a905b23b78e30890ab7a2bf6d3b43c20e13 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 27 Mar 2020 22:35:55 +1300 Subject: [PATCH 004/210] ff: Move pow_vartime into a trait that is generic over the limb size The trait is implemented by default for u8 and u64, allowing pow_vartime to be used with both the byte encoding and limb representation of field elements. --- bellman/src/domain.rs | 2 +- bellman/src/gadgets/multieq.rs | 2 +- bellman/src/gadgets/test/mod.rs | 2 +- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 8 ++-- bellman/src/groth16/tests/mod.rs | 12 +++--- ff/src/lib.rs | 35 +++++++++++++-- pairing/src/bls12_381/fq.rs | 52 ++++++++++++----------- pairing/src/bls12_381/fq2.rs | 6 +-- pairing/src/bls12_381/fr.rs | 8 ++-- pairing/src/bls12_381/mod.rs | 2 +- pairing/src/tests/engine.rs | 1 + pairing/src/tests/field.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 10 ++--- 14 files changed, 89 insertions(+), 55 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 0e9192e446..f1c25922fd 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -11,7 +11,7 @@ //! [`EvaluationDomain`]: crate::domain::EvaluationDomain //! [Groth16]: https://eprint.iacr.org/2016/260 -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Field, PowVartime, PrimeField, ScalarEngine}; use group::CurveProjective; use std::ops::{AddAssign, MulAssign, SubAssign}; diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs index 37b2d9429e..890eb7c6e7 100644 --- a/bellman/src/gadgets/multieq.rs +++ b/bellman/src/gadgets/multieq.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{PowVartime, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index 0a37cd16ff..a803accaef 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,6 +1,6 @@ //! Helpers for testing circuit implementations. -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use ff::{Field, PowVartime, PrimeField, PrimeFieldRepr, ScalarEngine}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 02efc21bd6..1d86992299 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -2,7 +2,7 @@ use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::Field; +use ff::{Field, PowVartime}; use group::{CurveAffine, CurveProjective, Wnaf}; use pairing::Engine; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 21322d86a9..4693aaa1a0 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,4 +1,6 @@ -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField}; +use ff::{ + Field, PowVartime, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField, +}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; @@ -190,9 +192,9 @@ impl SqrtField for Fr { // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) let mut c = Fr::root_of_unity(); // r = self^((t + 1) // 2) - let mut r = self.pow_vartime([32]); + let mut r = self.pow_vartime([32u64]); // t = self^t - let mut t = self.pow_vartime([63]); + let mut t = self.pow_vartime([63u64]); let mut m = Fr::S; while t != ::one() { diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index 5c2f02dbb2..2914bf2c04 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField}; +use ff::{Field, PowVartime, PrimeField}; use pairing::Engine; mod dummy_engine; @@ -127,22 +127,22 @@ fn test_xordemo() { let mut root_of_unity = Fr::root_of_unity(); // We expect this to be a 2^10 root of unity - assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 10])); + assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 10])); // Let's turn it into a 2^3 root of unity. - root_of_unity = root_of_unity.pow_vartime(&[1 << 7]); - assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 3])); + root_of_unity = root_of_unity.pow_vartime(&[1u64 << 7]); + assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 3])); assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity); // Let's compute all the points in our evaluation domain. let mut points = Vec::with_capacity(8); - for i in 0..8 { + for i in 0u64..8 { points.push(root_of_unity.pow_vartime(&[i])); } // Let's compute t(tau) = (tau - p_0)(tau - p_1)... // = tau^8 - 1 - let mut t_at_tau = tau.pow_vartime(&[8]); + let mut t_at_tau = tau.pow_vartime(&[8u64]); t_at_tau.sub_assign(&Fr::one()); { let mut tmp = Fr::one(); diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 65c32b2d64..e3cb8b4747 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -13,7 +13,7 @@ extern crate std; pub use ff_derive::*; use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use rand_core::RngCore; #[cfg(feature = "std")] use std::io::{self, Read, Write}; @@ -73,21 +73,36 @@ pub trait Field: /// Exponentiates this element by a power of the base prime modulus via /// the Frobenius automorphism. fn frobenius_map(&mut self, power: usize); +} + +pub trait PowVartime: Field +where + L: Copy + PartialEq + PartialOrd + AddAssign, + L: BitAnd, + L: Shr, + L: Sub, +{ + const ZERO: L; + const ONE: L; + const LIMB_SIZE: L; /// Exponentiates `self` by `exp`, where `exp` is a little-endian order /// integer exponent. /// /// **This operation is variable time with respect to the exponent.** If the /// exponent is fixed, this operation is effectively constant time. - fn pow_vartime>(&self, exp: S) -> Self { + fn pow_vartime>(&self, exp: S) -> Self { let mut res = Self::one(); for e in exp.as_ref().iter().rev() { - for i in (0..64).rev() { + let mut i = Self::ZERO; + while i < Self::LIMB_SIZE { res = res.square(); - if ((*e >> i) & 1) == 1 { + if ((*e >> (Self::LIMB_SIZE - Self::ONE - i)) & Self::ONE) == Self::ONE { res.mul_assign(self); } + + i += Self::ONE; } } @@ -95,6 +110,18 @@ pub trait Field: } } +impl PowVartime for T { + const ZERO: u8 = 0; + const ONE: u8 = 1; + const LIMB_SIZE: u8 = 8; +} + +impl PowVartime for T { + const ZERO: u64 = 0; + const ONE: u64 = 1; + const LIMB_SIZE: u64 = 64; +} + /// This trait represents an element of a field that has a square root operation described for it. pub trait SqrtField: Field { /// Returns the square root of the field element, if it is diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 57d6532645..2f7b15d169 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -2,6 +2,8 @@ use super::fq2::Fq2; use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; use std::ops::{AddAssign, MulAssign, SubAssign}; +#[cfg(test)] +use ff::PowVartime; #[cfg(test)] use std::ops::Neg; @@ -466,7 +468,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ2_C1[1], nqr.pow_vartime([ - 0xdcff7fffffffd555, + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, @@ -484,7 +486,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C1[1], nqr.pow_vartime([ - 0x9354ffffffffe38e, + 0x9354ffffffffe38eu64, 0xa395554e5c6aaaa, 0xcd104635a790520c, 0xcc27c3d6fbd7063f, @@ -495,7 +497,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C1[2], nqr.pow_vartime([ - 0xb78e0000097b2f68, + 0xb78e0000097b2f68u64, 0xd44f23b47cbd64e3, 0x5cb9668120b069a9, 0xccea85f9bf7b3d16, @@ -512,7 +514,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C1[3], nqr.pow_vartime([ - 0xdbc6fcd6f35b9e06, + 0xdbc6fcd6f35b9e06u64, 0x997dead10becd6aa, 0x9dbbd24c17206460, 0x72b97acc6057c45e, @@ -535,7 +537,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C1[4], nqr.pow_vartime([ - 0x4649add3c71c6d90, + 0x4649add3c71c6d90u64, 0x43caa6528972a865, 0xcda8445bbaaa0fbb, 0xc93dea665662aa66, @@ -564,7 +566,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C1[5], nqr.pow_vartime([ - 0xf896f792732eb2be, + 0xf896f792732eb2beu64, 0x49c86a6d1dc593a1, 0xe5b31e94581f91c3, 0xe3da5cc0a6b20d7f, @@ -601,7 +603,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C2[1], nqr.pow_vartime([ - 0x26a9ffffffffc71c, + 0x26a9ffffffffc71cu64, 0x1472aaa9cb8d5555, 0x9a208c6b4f20a418, 0x984f87adf7ae0c7f, @@ -612,7 +614,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C2[2], nqr.pow_vartime([ - 0x6f1c000012f65ed0, + 0x6f1c000012f65ed0u64, 0xa89e4768f97ac9c7, 0xb972cd024160d353, 0x99d50bf37ef67a2c, @@ -629,7 +631,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C2[3], nqr.pow_vartime([ - 0xb78df9ade6b73c0c, + 0xb78df9ade6b73c0cu64, 0x32fbd5a217d9ad55, 0x3b77a4982e40c8c1, 0xe572f598c0af88bd, @@ -652,7 +654,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C2[4], nqr.pow_vartime([ - 0x8c935ba78e38db20, + 0x8c935ba78e38db20u64, 0x87954ca512e550ca, 0x9b5088b775541f76, 0x927bd4ccacc554cd, @@ -681,7 +683,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ6_C2[5], nqr.pow_vartime([ - 0xf12def24e65d657c, + 0xf12def24e65d657cu64, 0x9390d4da3b8b2743, 0xcb663d28b03f2386, 0xc7b4b9814d641aff, @@ -718,7 +720,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[1], nqr.pow_vartime([ - 0x49aa7ffffffff1c7, + 0x49aa7ffffffff1c7u64, 0x51caaaa72e35555, 0xe688231ad3c82906, 0xe613e1eb7deb831f, @@ -729,7 +731,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[2], nqr.pow_vartime([ - 0xdbc7000004bd97b4, + 0xdbc7000004bd97b4u64, 0xea2791da3e5eb271, 0x2e5cb340905834d4, 0xe67542fcdfbd9e8b, @@ -746,7 +748,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[3], nqr.pow_vartime(vec![ - 0x6de37e6b79adcf03, + 0x6de37e6b79adcf03u64, 0x4cbef56885f66b55, 0x4edde9260b903230, 0x395cbd66302be22f, @@ -769,7 +771,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[4], nqr.pow_vartime(vec![ - 0xa324d6e9e38e36c8, + 0xa324d6e9e38e36c8u64, 0xa1e5532944b95432, 0x66d4222ddd5507dd, 0xe49ef5332b315533, @@ -798,7 +800,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[5], nqr.pow_vartime(vec![ - 0xfc4b7bc93997595f, + 0xfc4b7bc93997595fu64, 0xa4e435368ee2c9d0, 0xf2d98f4a2c0fc8e1, 0xf1ed2e60535906bf, @@ -833,7 +835,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[6], nqr.pow_vartime(vec![ - 0x21219610a012ba3c, + 0x21219610a012ba3cu64, 0xa5c19ad35375325, 0x4e9df1e497674396, 0xfb05b717c991c6ef, @@ -874,7 +876,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[7], nqr.pow_vartime(vec![ - 0x742754a1f22fdb, + 0x742754a1f22fdbu64, 0x2a1955c2dec3a702, 0x9747b28c796d134e, 0xc113a0411f59db79, @@ -921,7 +923,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[8], nqr.pow_vartime(vec![ - 0x802f5720d0b25710, + 0x802f5720d0b25710u64, 0x6714f0a258b85c7c, 0x31394c90afdf16e, 0xe9d2b0c64f957b19, @@ -974,7 +976,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[9], nqr.pow_vartime(vec![ - 0x4af4accf7de0b977, + 0x4af4accf7de0b977u64, 0x742485e21805b4ee, 0xee388fbc4ac36dec, 0x1e199da57ad178a, @@ -1033,7 +1035,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[10], nqr.pow_vartime(vec![ - 0xe5953a4f96cdda44, + 0xe5953a4f96cdda44u64, 0x336b2d734cbc32bb, 0x3f79bfe3cd7410e, 0x267ae19aaa0f0332, @@ -1098,7 +1100,7 @@ fn test_frob_coeffs() { assert_eq!( FROBENIUS_COEFF_FQ12_C1[11], nqr.pow_vartime(vec![ - 0x107db680942de533, + 0x107db680942de533u64, 0x6262b24d2052393b, 0x6136df824159ebc, 0xedb052c9970c5deb, @@ -2029,7 +2031,7 @@ fn test_fq_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fq::random(&mut rng); @@ -2197,7 +2199,7 @@ fn test_fq_root_of_unity() { ); assert_eq!( Fq::multiplicative_generator().pow_vartime([ - 0xdcff7fffffffd555, + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, @@ -2206,7 +2208,7 @@ fn test_fq_root_of_unity() { ]), Fq::root_of_unity() ); - assert_eq!(Fq::root_of_unity().pow_vartime([1 << Fq::S]), Fq::one()); + assert_eq!(Fq::root_of_unity().pow_vartime([1u64 << Fq::S]), Fq::one()); assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none())); } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 0cd88e7d7a..e0955f08f2 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -1,5 +1,5 @@ use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::{Field, SqrtField}; +use ff::{Field, PowVartime, SqrtField}; use rand_core::RngCore; use std::cmp::Ordering; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -254,7 +254,7 @@ impl SqrtField for Fq2 { } else { // a1 = self^((q - 3) / 4) let mut a1 = self.pow_vartime([ - 0xee7fbfffffffeaaa, + 0xee7fbfffffffeaaau64, 0x7aaffffac54ffff, 0xd9cc34a83dac3d89, 0xd91dd2e13ce144af, @@ -286,7 +286,7 @@ impl SqrtField for Fq2 { alpha.add_assign(&Fq2::one()); // alpha = alpha^((q - 1) / 2) alpha = alpha.pow_vartime([ - 0xdcff7fffffffd555, + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 4c30e49cb0..f520d303e2 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -6,6 +6,8 @@ use std::ops::{AddAssign, MulAssign, SubAssign}; #[PrimeFieldGenerator = "7"] pub struct Fr(FrRepr); +#[cfg(test)] +use ff::PowVartime; #[cfg(test)] use rand_core::SeedableRng; #[cfg(test)] @@ -763,7 +765,7 @@ fn test_fr_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fr::random(&mut rng); @@ -965,14 +967,14 @@ fn test_fr_root_of_unity() { ); assert_eq!( Fr::multiplicative_generator().pow_vartime([ - 0xfffe5bfeffffffff, + 0xfffe5bfeffffffffu64, 0x9a1d80553bda402, 0x299d7d483339d808, 0x73eda753 ]), Fr::root_of_unity() ); - assert_eq!(Fr::root_of_unity().pow_vartime([1 << Fr::S]), Fr::one()); + assert_eq!(Fr::root_of_unity().pow_vartime([1u64 << Fr::S]), Fr::one()); assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none())); } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 80848e12dc..afa3aaf0e6 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -23,7 +23,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, PairingCurveAffine}; -use ff::{BitIterator, Field, ScalarEngine}; +use ff::{BitIterator, Field, PowVartime, ScalarEngine}; use group::CurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 0776e5d4b1..c65816de1a 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,3 +1,4 @@ +use ff::PowVartime; use group::{CurveAffine, CurveProjective}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index 7ddb365347..a1f72b84bd 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PowVartime, PrimeField, SqrtField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index ef1ccbea46..6e902cbd3b 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,6 +1,6 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::{ - adc, mac_with_carry, sbb, BitIterator, Field, PrimeField, PrimeFieldDecodingError, + adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField, }; use rand_core::RngCore; @@ -745,7 +745,7 @@ impl SqrtField for Fs { // a1 = self^((s - 3) // 4) let mut a1 = self.pow_vartime([ - 0xb425c397b5bdcb2d, + 0xb425c397b5bdcb2du64, 0x299a0824f3320420, 0x4199cec0404d0ec0, 0x39f6d3a994cebea, @@ -1491,7 +1491,7 @@ fn test_fs_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fs::random(&mut rng); @@ -1689,13 +1689,13 @@ fn test_fs_root_of_unity() { ); assert_eq!( Fs::multiplicative_generator().pow_vartime([ - 0x684b872f6b7b965b, + 0x684b872f6b7b965bu64, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4 ]), Fs::root_of_unity() ); - assert_eq!(Fs::root_of_unity().pow_vartime([1 << Fs::S]), Fs::one()); + assert_eq!(Fs::root_of_unity().pow_vartime([1u64 << Fs::S]), Fs::one()); assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none())); } From 3ccadf301751b7a0ac52a5e499f665ef76f1e9e7 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Fri, 3 Apr 2020 12:13:39 -0700 Subject: [PATCH 005/210] Add binding signature only if needed --- zcash_primitives/src/transaction/builder.rs | 90 +++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9acb996d41..fda58ee846 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -511,6 +511,9 @@ impl Builder { tx_metadata.spend_indices.resize(spends.len(), 0); tx_metadata.output_indices.resize(orig_outputs_len, 0); + // Record if we'll need a binding signature + let binding_sig_needed = !spends.is_empty() || !outputs.is_empty(); + // Create Sapling SpendDescriptions if !spends.is_empty() { let anchor = self.anchor.expect("anchor was set if spends were added"); @@ -644,11 +647,17 @@ impl Builder { &JUBJUB, )); } - self.mtx.binding_sig = Some( - prover - .binding_sig(&mut ctx, self.mtx.value_balance, &sighash) - .map_err(|()| Error::BindingSig)?, - ); + + // Add a binding signature if needed + if binding_sig_needed { + self.mtx.binding_sig = Some( + prover + .binding_sig(&mut ctx, self.mtx.value_balance, &sighash) + .map_err(|()| Error::BindingSig)?, + ); + } else { + self.mtx.binding_sig = None; + } // Transparent signatures self.transparent_inputs @@ -694,6 +703,77 @@ mod tests { ); } + #[test] + fn binding_sig_absent_if_no_shielded_spend_or_output() { + use crate::transaction::{ + builder::{self, TransparentInputs}, + TransactionData, + }; + + // Create a builder with 0 fee, so we can construct t outputs + let mut builder = builder::Builder { + rng: OsRng, + mtx: TransactionData::new(), + fee: Amount::zero(), + anchor: None, + spends: vec![], + outputs: vec![], + transparent_inputs: TransparentInputs::default(), + change_address: None, + }; + + // Create a tx with only t output. No binding_sig should be present + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + let (tx, _) = builder + .build(consensus::BranchId::Sapling, &MockTxProver) + .unwrap(); + // No binding signature, because only t input and outputs + assert!(tx.binding_sig.is_none()); + } + + #[test] + fn binding_sig_present_if_shielded_spend() { + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let to = extfvk.default_address().unwrap().1; + + let mut rng = OsRng; + + let note1 = to + .create_note(50000, Fs::random(&mut rng), &JUBJUB) + .unwrap(); + let cm1 = Node::new(note1.cm(&JUBJUB).into_repr()); + let mut tree = CommitmentTree::new(); + tree.append(cm1).unwrap(); + let witness1 = IncrementalWitness::from_tree(&tree); + + let mut builder = Builder::new(0); + + // Create a tx with a sapling spend. binding_sig should be present + builder + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1.clone(), + witness1.path().unwrap(), + ) + .unwrap(); + + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + // Expect a binding signature error, because our inputs aren't valid, but this shows + // that a binding signature was attempted + assert_eq!( + builder.build(consensus::BranchId::Sapling, &MockTxProver), + Err(Error::BindingSig) + ); + } + #[test] fn fails_on_negative_transparent_output() { let mut builder = Builder::new(0); From fd79de5408997899f9d183f92f01baf49a9a406f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 27 Mar 2020 23:19:58 +1300 Subject: [PATCH 006/210] ff: Add PrimeField: From constraint --- bellman/src/groth16/tests/dummy_engine.rs | 6 ++++++ ff/ff_derive/src/lib.rs | 9 +++++++++ ff/src/lib.rs | 6 +++--- pairing/src/bls12_381/fq.rs | 13 ++++--------- pairing/src/bls12_381/fq2.rs | 2 +- pairing/src/bls12_381/fr.rs | 7 ++----- pairing/src/bls12_381/tests/mod.rs | 18 +++++++++--------- pairing/src/tests/field.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 16 +++++++++++----- zcash_primitives/src/primitives.rs | 4 ++-- zcash_proofs/src/sapling/mod.rs | 4 ++-- 11 files changed, 50 insertions(+), 37 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 4693aaa1a0..2ef9798c80 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -34,6 +34,12 @@ impl fmt::Display for Fr { } } +impl From for Fr { + fn from(v: u64) -> Fr { + Fr(Wrapping((v % MODULUS_R.0 as u64) as u32)) + } +} + impl ConditionallySelectable for Fr { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Fr(Wrapping(u32::conditional_select( diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 121c296dc7..9a8a744620 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -853,6 +853,15 @@ fn prime_field_impl( } } + impl From for #name { + #[inline(always)] + fn from(val: u64) -> #name { + let mut raw = [0u64; #limbs]; + raw[0] = val; + #name(#repr(raw)) * #name(R2) + } + } + impl From<#name> for #repr { fn from(e: #name) -> #repr { e.into_repr() diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e3cb8b4747..8bb8ffabce 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -256,7 +256,7 @@ impl fmt::Display for PrimeFieldDecodingError { } /// This represents an element of a prime field. -pub trait PrimeField: Field { +pub trait PrimeField: Field + From { /// The prime field can be converted back and forth into this biginteger /// representation. type Repr: PrimeFieldRepr + From; @@ -274,7 +274,7 @@ pub trait PrimeField: Field { let mut res = Self::zero(); - let ten = Self::from_repr(Self::Repr::from(10)).unwrap(); + let ten = Self::from(10); let mut first_digit = true; @@ -290,7 +290,7 @@ pub trait PrimeField: Field { } res.mul_assign(&ten); - res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap()); + res.add_assign(&Self::from(u64::from(c))); } None => { return None; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 2f7b15d169..5a8f173076 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -456,7 +456,7 @@ pub struct Fq(FqRepr); #[test] fn test_b_coeff() { - assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); + assert_eq!(Fq::from(4), B_COEFF); } #[test] @@ -1586,7 +1586,7 @@ fn test_fq_is_valid() { assert!(!a.is_valid()); a.0.sub_noborrow(&FqRepr::from(1)); assert!(a.is_valid()); - assert!(Fq(FqRepr::from(0)).is_valid()); + assert!(Fq::from(0).is_valid()); assert!(Fq(FqRepr([ 0xdf4671abd14dab3e, 0xe2dc0c9f534fbd33, @@ -2193,10 +2193,7 @@ fn test_fq_root_of_unity() { use ff::SqrtField; assert_eq!(Fq::S, 1); - assert_eq!( - Fq::multiplicative_generator(), - Fq::from_repr(FqRepr::from(2)).unwrap() - ); + assert_eq!(Fq::multiplicative_generator(), Fq::from(2)); assert_eq!( Fq::multiplicative_generator().pow_vartime([ 0xdcff7fffffffd555u64, @@ -2225,9 +2222,7 @@ fn test_fq_ordering() { // FqRepr's ordering is well-tested, but we still need to make sure the Fq // elements aren't being compared in Montgomery form. for i in 0..100 { - assert!( - Fq::from_repr(FqRepr::from(i + 1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap() - ); + assert!(Fq::from(i + 1) > Fq::from(i)); } } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index e0955f08f2..6e6230736f 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -364,7 +364,7 @@ fn test_fq2_squaring() { a.square(), Fq2 { c0: Fq::zero(), - c1: Fq::from_repr(FqRepr::from(2)).unwrap(), + c1: Fq::from(2), } ); // 2u diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index f520d303e2..028287d209 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -368,7 +368,7 @@ fn test_fr_is_valid() { assert!(!a.is_valid()); a.0.sub_noborrow(&FrRepr::from(1)); assert!(a.is_valid()); - assert!(Fr(FrRepr::from(0)).is_valid()); + assert!(Fr::from(0).is_valid()); assert!(Fr(FrRepr([ 0xffffffff00000000, 0x53bda402fffe5bfe, @@ -961,10 +961,7 @@ fn test_fr_root_of_unity() { use ff::SqrtField; assert_eq!(Fr::S, 32); - assert_eq!( - Fr::multiplicative_generator(), - Fr::from_repr(FrRepr::from(7)).unwrap() - ); + assert_eq!(Fr::multiplicative_generator(), Fr::from(7)); assert_eq!( Fr::multiplicative_generator().pow_vartime([ 0xfffe5bfeffffffffu64, diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 9c5b2c93ff..f79961cdbc 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -191,7 +191,7 @@ fn test_g1_uncompressed_invalid_vectors() { loop { let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? let y = x3b.sqrt(); if y.is_some().into() { @@ -331,8 +331,8 @@ fn test_g2_uncompressed_invalid_vectors() { let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? let y = x3b.sqrt(); @@ -428,7 +428,7 @@ fn test_g1_compressed_invalid_vectors() { loop { let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? if x3b.sqrt().is_some().into() { x.add_assign(&Fq::one()); @@ -452,7 +452,7 @@ fn test_g1_compressed_invalid_vectors() { loop { let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. @@ -558,8 +558,8 @@ fn test_g2_compressed_invalid_vectors() { let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? if x3b.sqrt().is_some().into() { @@ -589,8 +589,8 @@ fn test_g2_compressed_invalid_vectors() { let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? if x3b.sqrt().is_some().into() { diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index a1f72b84bd..64242322e1 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -119,7 +119,7 @@ pub fn from_str_tests() { let n = rng.next_u64(); let a = F::from_str(&format!("{}", n)).unwrap(); - let b = F::from_repr(n.into()).unwrap(); + let b = F::from(n); assert_eq!(a, b); } diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 6e902cbd3b..466d4c5ea9 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -278,6 +278,15 @@ impl ::std::fmt::Display for Fs { } } +impl From for Fs { + #[inline(always)] + fn from(val: u64) -> Fs { + let mut raw = [0u64; 4]; + raw[0] = val; + Fs(FsRepr(raw)) * Fs(R2) + } +} + impl From for FsRepr { fn from(e: Fs) -> FsRepr { e.into_repr() @@ -514,7 +523,7 @@ impl Field for Fs { #[inline] fn zero() -> Self { - Fs(FsRepr::from(0)) + Fs::from(0) } #[inline] @@ -1683,10 +1692,7 @@ fn test_fs_num_bits() { #[test] fn test_fs_root_of_unity() { assert_eq!(Fs::S, 1); - assert_eq!( - Fs::multiplicative_generator(), - Fs::from_repr(FsRepr::from(6)).unwrap() - ); + assert_eq!(Fs::multiplicative_generator(), Fs::from(6)); assert_eq!( Fs::multiplicative_generator().pow_vartime([ 0x684b872f6b7b965bu64, diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index af4fa3ad09..6e01a1052c 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -24,7 +24,7 @@ impl ValueCommitment { pub fn cm(&self, params: &E::Params) -> edwards::Point { params .generator(FixedGenerators::ValueCommitmentValue) - .mul(self.value, params) + .mul(E::Fs::from(self.value), params) .add( ¶ms .generator(FixedGenerators::ValueCommitmentRandomness) @@ -291,7 +291,7 @@ impl Note { let rho = self.cm_full_point(params).add( ¶ms .generator(FixedGenerators::NullifierPosition) - .mul(position, params), + .mul(E::Fs::from(position), params), params, ); diff --git a/zcash_proofs/src/sapling/mod.rs b/zcash_proofs/src/sapling/mod.rs index 60ffd9bdec..cd37578744 100644 --- a/zcash_proofs/src/sapling/mod.rs +++ b/zcash_proofs/src/sapling/mod.rs @@ -2,7 +2,7 @@ use pairing::bls12_381::Bls12; use zcash_primitives::jubjub::{ - edwards, fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown, + edwards, fs::Fs, FixedGenerators, JubjubBls12, JubjubParams, Unknown, }; use zcash_primitives::transaction::components::Amount; @@ -30,7 +30,7 @@ fn compute_value_balance( // Compute it in the exponent let mut value_balance = params .generator(FixedGenerators::ValueCommitmentValue) - .mul(FsRepr::from(abs), params); + .mul(Fs::from(abs), params); // Negate if necessary if is_negative { From 232f0a50b8c95a32fc2dc557179b5cd7f257cf45 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 28 Mar 2020 12:02:32 +1300 Subject: [PATCH 007/210] ff: Rework BitIterator to work with both u8 and u64 limb sizes This enables BitIterator to be used with both the byte encoding and limb representation of scalars. --- bellman/src/gadgets/boolean.rs | 4 +- bellman/src/gadgets/num.rs | 8 ++-- ff/src/lib.rs | 46 ++++++++++++++++++++--- pairing/src/bls12_381/ec.rs | 27 +++++++++---- pairing/src/bls12_381/mod.rs | 4 +- zcash_primitives/src/jubjub/edwards.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 7 +--- zcash_primitives/src/jubjub/montgomery.rs | 2 +- zcash_primitives/src/sapling.rs | 4 +- zcash_proofs/src/circuit/ecc.rs | 4 +- zcash_proofs/src/circuit/sapling.rs | 8 ++-- 11 files changed, 80 insertions(+), 36 deletions(-) diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index d3c882d112..f11768164e 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -313,12 +313,12 @@ pub fn field_into_allocated_bits_le, F: // Deconstruct in big-endian bit order let values = match value { Some(ref value) => { - let mut field_char = BitIterator::new(F::char()); + let mut field_char = BitIterator::::new(F::char()); let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); let mut found_one = false; - for b in BitIterator::new(value.into_repr()) { + for b in BitIterator::::new(value.into_repr()) { // Skip leading bits found_one |= field_char.next().unwrap(); if !found_one { diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index e460d201c1..f8ce6d32dc 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -103,7 +103,9 @@ impl AllocatedNum { // We want to ensure that the bit representation of a is // less than or equal to r - 1. - let mut a = self.value.map(|e| BitIterator::new(e.into_repr())); + let mut a = self + .value + .map(|e| BitIterator::::new(e.into_repr())); let mut b = E::Fr::char(); b.sub_noborrow(&1.into()); @@ -115,7 +117,7 @@ impl AllocatedNum { let mut found_one = false; let mut i = 0; - for b in BitIterator::new(b) { + for b in BitIterator::::new(b) { let a_bit = a.as_mut().map(|e| e.next().unwrap()); // Skip over unset bits at the beginning @@ -558,7 +560,7 @@ mod test { assert!(cs.is_satisfied()); - for (b, a) in BitIterator::new(r.into_repr()) + for (b, a) in BitIterator::::new(r.into_repr()) .skip(1) .zip(bits.iter().rev()) { diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 8bb8ffabce..e91210fc9a 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -13,6 +13,7 @@ extern crate std; pub use ff_derive::*; use core::fmt; +use core::marker::PhantomData; use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use rand_core::RngCore; #[cfg(feature = "std")] @@ -338,20 +339,25 @@ pub trait ScalarEngine: Sized + 'static + Clone { } #[derive(Debug)] -pub struct BitIterator { +pub struct BitIterator> { t: E, n: usize, + _limb: PhantomData, } -impl> BitIterator { +impl> BitIterator { pub fn new(t: E) -> Self { let n = t.as_ref().len() * 64; - BitIterator { t, n } + BitIterator { + t, + n, + _limb: PhantomData::default(), + } } } -impl> Iterator for BitIterator { +impl> Iterator for BitIterator { type Item = bool; fn next(&mut self) -> Option { @@ -367,9 +373,37 @@ impl> Iterator for BitIterator { } } +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 8; + + BitIterator { + t, + n, + _limb: PhantomData::default(), + } + } +} + +impl> Iterator for BitIterator { + type Item = bool; + + fn next(&mut self) -> Option { + if self.n == 0 { + None + } else { + self.n -= 1; + let part = self.n / 8; + let bit = self.n - (8 * part); + + Some(self.t.as_ref()[part] & (1 << bit) > 0) + } + } +} + #[test] fn test_bit_iterator() { - let mut a = BitIterator::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]); + let mut a = BitIterator::::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]); let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001"; for e in expected.chars() { @@ -380,7 +414,7 @@ fn test_bit_iterator() { let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001"; - let mut a = BitIterator::new([ + let mut a = BitIterator::::new([ 0x429d_5f3a_c3a3_b759, 0xb10f_4c66_768b_1c92, 0x9236_8b6d_16ec_d3b4, diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 2dae1ea0e8..42bd91e20f 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -81,7 +81,18 @@ macro_rules! curve_impl { } impl $affine { - fn mul_bits>(&self, bits: BitIterator) -> $projective { + fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { + let mut res = $projective::zero(); + for i in bits { + res.double(); + if i { + res.add_assign(self) + } + } + res + } + + fn mul_bits_u8>(&self, bits: BitIterator) -> $projective { let mut res = $projective::zero(); for i in bits { res.double(); @@ -172,8 +183,8 @@ macro_rules! curve_impl { } fn mul::Repr>>(&self, by: S) -> $projective { - let bits = BitIterator::new(by.into()); - self.mul_bits(bits) + let bits = BitIterator::::new(by.into()); + self.mul_bits_u64(bits) } fn into_projective(&self) -> $projective { @@ -655,7 +666,7 @@ macro_rules! curve_impl { let mut found_one = false; - for i in BitIterator::new(other.into()) { + for i in BitIterator::::new(other.into()) { if found_one { res.double(); } else { @@ -992,8 +1003,8 @@ pub mod g1 { impl G1Affine { fn scale_by_cofactor(&self) -> G1 { // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 - let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); - self.mul_bits(cofactor) + let cofactor = BitIterator::::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); + self.mul_bits_u64(cofactor) } fn get_generator() -> Self { @@ -1714,7 +1725,7 @@ pub mod g2 { fn scale_by_cofactor(&self) -> G2 { // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 - let cofactor = BitIterator::new([ + let cofactor = BitIterator::::new([ 0xcf1c38e31c7238e5, 0x1616ec6e786f0c70, 0x21537e293a6691ae, @@ -1724,7 +1735,7 @@ pub mod g2 { 0x91d50792876a202, 0x5d543a95414e7f1, ]); - self.mul_bits(cofactor) + self.mul_bits_u64(cofactor) } fn perform_pairing(&self, other: &G1Affine) -> Fq12 { diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index afa3aaf0e6..0f18053215 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -82,7 +82,7 @@ impl Engine for Bls12 { let mut f = Fq12::one(); let mut found_one = false; - for i in BitIterator::new(&[BLS_X >> 1]) { + for i in BitIterator::::new(&[BLS_X >> 1]) { if !found_one { found_one = i; continue; @@ -324,7 +324,7 @@ impl G2Prepared { let mut r: G2 = q.into(); let mut found_one = false; - for i in BitIterator::new([BLS_X >> 1]) { + for i in BitIterator::::new([BLS_X >> 1]) { if !found_one { found_one = i; continue; diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index 1b3ebc0b65..549d44140f 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -468,7 +468,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 466d4c5ea9..e163adc374 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,4 +1,3 @@ -use byteorder::{ByteOrder, LittleEndian}; use ff::{ adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField, @@ -721,7 +720,7 @@ impl Fs { self.reduce(); } - fn mul_bits>(&self, bits: BitIterator) -> Self { + fn mul_bits>(&self, bits: BitIterator) -> Self { let mut res = Self::zero(); for bit in bits { res = res.double(); @@ -741,9 +740,7 @@ impl ToUniform for Fs { /// Random Oracle output. fn to_uniform(digest: &[u8]) -> Self { assert_eq!(digest.len(), 64); - let mut repr: [u64; 8] = [0; 8]; - LittleEndian::read_u64_into(digest, &mut repr); - Self::one().mul_bits(BitIterator::new(repr)) + Self::one().mul_bits(BitIterator::::new(digest)) } } diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index 9cad803e82..efdb29dcda 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -304,7 +304,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index da8b8384f7..ecf5cd4007 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -21,7 +21,7 @@ pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let lhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(lhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { *a = b; } tmp @@ -29,7 +29,7 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let rhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(rhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(rhs)) { *a = b; } tmp diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 05baf8b607..01ed2d436b 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -769,7 +769,7 @@ mod test { let q = p.mul(s, params); let (x1, y1) = q.to_xy(); - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); @@ -822,7 +822,7 @@ mod test { y: num_y0, }; - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 9782a4f965..c3ddde9c63 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -615,8 +615,8 @@ fn test_input_circuit_with_bls12_381() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); lhs.reverse(); rhs.reverse(); @@ -799,8 +799,8 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); lhs.reverse(); rhs.reverse(); From 1fdca393bb1eeeabbdd46215a5281e93bd7a95d6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 21 Apr 2020 19:05:19 +1200 Subject: [PATCH 008/210] ff: PrimeField::{is_even, is_odd} --- bellman/src/groth16/tests/dummy_engine.rs | 4 ++++ ff/ff_derive/src/lib.rs | 5 +++++ ff/src/lib.rs | 9 +++++++++ pairing/src/bls12_381/fq.rs | 12 ++++++++++++ pairing/src/bls12_381/fr.rs | 12 ++++++++++++ zcash_primitives/src/jubjub/edwards.rs | 5 ++--- zcash_primitives/src/jubjub/fs.rs | 5 +++++ zcash_primitives/src/jubjub/montgomery.rs | 4 ++-- zcash_primitives/src/jubjub/tests.rs | 2 +- zcash_proofs/src/circuit/sapling.rs | 4 ++-- 10 files changed, 54 insertions(+), 8 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 2ef9798c80..91f6993d97 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -332,6 +332,10 @@ impl PrimeField for Fr { FrRepr::from(*self) } + fn is_odd(&self) -> bool { + (self.0).0 % 2 != 0 + } + fn char() -> FrRepr { Fr(MODULUS_R).into() } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 9a8a744620..17ad39917e 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -1029,6 +1029,11 @@ fn prime_field_impl( r.0 } + #[inline(always)] + fn is_odd(&self) -> bool { + self.into_repr().is_odd() + } + fn char() -> #repr { MODULUS } diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e91210fc9a..b2f4d3af65 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -309,6 +309,15 @@ pub trait PrimeField: Field + From { /// the number is an element of the field. fn into_repr(&self) -> Self::Repr; + /// Returns true iff this element is odd. + fn is_odd(&self) -> bool; + + /// Returns true iff this element is even. + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + /// Returns the field characteristic; the modulus. fn char() -> Self::Repr; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 5a8f173076..8e5d660286 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -2182,6 +2182,18 @@ fn test_fq_display() { ); } +#[test] +fn test_fq_is_odd() { + assert!(!Fq::from(0).is_odd()); + assert!(Fq::from(0).is_even()); + assert!(Fq::from(1).is_odd()); + assert!(!Fq::from(1).is_even()); + assert!(!Fq::from(324834872).is_odd()); + assert!(Fq::from(324834872).is_even()); + assert!(Fq::from(324834873).is_odd()); + assert!(!Fq::from(324834873).is_even()); +} + #[test] fn test_fq_num_bits() { assert_eq!(Fq::NUM_BITS, 381); diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 028287d209..cc02d220e9 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -950,6 +950,18 @@ fn test_fr_display() { ); } +#[test] +fn test_fr_is_odd() { + assert!(!Fr::from(0).is_odd()); + assert!(Fr::from(0).is_even()); + assert!(Fr::from(1).is_odd()); + assert!(!Fr::from(1).is_even()); + assert!(!Fr::from(324834872).is_odd()); + assert!(Fr::from(324834872).is_even()); + assert!(Fr::from(324834873).is_odd()); + assert!(!Fr::from(324834873).is_even()); +} + #[test] fn test_fr_num_bits() { assert_eq!(Fr::NUM_BITS, 255); diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index 549d44140f..cbe4d0be0c 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -127,7 +127,7 @@ impl Point { tmp1.mul_assign(&tmp2); tmp1.sqrt().map(|mut x| { - if x.into_repr().is_odd() != sign { + if x.is_odd() != sign { x = x.neg(); } @@ -172,9 +172,8 @@ impl Point { 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() { + if x.is_odd() { y_repr.as_mut()[3] |= 0x8000000000000000u64; } diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index e163adc374..e4bea138d3 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -481,6 +481,11 @@ impl PrimeField for Fs { r.0 } + #[inline(always)] + fn is_odd(&self) -> bool { + self.into_repr().is_odd() + } + fn char() -> FsRepr { MODULUS } diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index efdb29dcda..0992637fe4 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -1,4 +1,4 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{BitIterator, Field, PrimeField, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; @@ -60,7 +60,7 @@ impl Point { rhs.add_assign(&x2); rhs.sqrt().map(|mut y| { - if y.into_repr().is_odd() != sign { + if y.is_odd() != sign { y = y.neg(); } diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index 84b3a96497..6f66c44466 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -237,7 +237,7 @@ fn test_get_for(params: &E::Params) { let p = edwards::Point::::get_for_y(y, sign, params); if bool::from(p.is_some()) { let mut p = p.unwrap(); - assert!(p.to_xy().0.into_repr().is_odd() == sign); + assert!(p.to_xy().0.is_odd() == sign); p = p.negate(); assert!(edwards::Point::::get_for_y(y, !sign, params).unwrap() == p); } diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index c3ddde9c63..7d1fbbabdc 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -1,6 +1,6 @@ //! The Sapling circuits. -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use bellman::{Circuit, ConstraintSystem, SynthesisError}; @@ -478,7 +478,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( cs.namespace(|| "pk_d bit of x"), - pk_d.map(|e| e.0.into_repr().is_odd()), + pk_d.map(|e| e.0.is_odd()), )?); // Extend the note with pk_d representation From 08500ee71275e7b6127e7b512c0763b058acf714 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Apr 2020 10:45:51 +1200 Subject: [PATCH 009/210] ff: PrimeField: BitAnd + Shr --- bellman/src/groth16/tests/dummy_engine.rs | 19 +++- ff/ff_derive/src/lib.rs | 73 +++++++++++-- ff/src/lib.rs | 4 +- pairing/src/bls12_381/fq.rs | 73 +++++++++++++ pairing/src/bls12_381/fr.rs | 61 +++++++++++ zcash_primitives/src/jubjub/fs.rs | 127 +++++++++++++++++++++- zcash_primitives/src/pedersen_hash.rs | 8 +- 7 files changed, 348 insertions(+), 17 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 91f6993d97..86e7b182eb 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -8,7 +8,7 @@ use rand_core::RngCore; use std::cmp::Ordering; use std::fmt; use std::num::Wrapping; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; const MODULUS_R: Wrapping = Wrapping(64513); @@ -151,6 +151,23 @@ impl MulAssign for Fr { } } +impl BitAnd for Fr { + type Output = u64; + + fn bitand(self, rhs: u64) -> u64 { + (self.0).0 as u64 & rhs + } +} + +impl Shr for Fr { + type Output = Fr; + + fn shr(mut self, rhs: u32) -> Fr { + self.0 = Wrapping((self.0).0 >> rhs); + self + } +} + impl Field for Fr { fn random(rng: &mut R) -> Self { Fr(Wrapping(rng.next_u32()) % MODULUS_R) diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 17ad39917e..f5439c5357 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -785,14 +785,19 @@ fn prime_field_impl( proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), ); - // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... - let mut into_repr_params = proc_macro2::TokenStream::new(); - into_repr_params.append_separated( - (0..limbs) - .map(|i| quote! { (self.0).0[#i] }) - .chain((0..limbs).map(|_| quote! {0})), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); + fn mont_reduce_params(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { + // a.0[0], a.0[1], ..., 0, 0, 0, 0, ... + let mut mont_reduce_params = proc_macro2::TokenStream::new(); + mont_reduce_params.append_separated( + (0..limbs) + .map(|i| quote! { (#a.0).0[#i] }) + .chain((0..limbs).map(|_| quote! {0})), + proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), + ); + mont_reduce_params + } + + let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); let top_limb_index = limbs - 1; @@ -1006,6 +1011,56 @@ fn prime_field_impl( } } + impl ::core::ops::BitAnd for #name { + type Output = u64; + + #[inline(always)] + fn bitand(mut self, rhs: u64) -> u64 { + self.mont_reduce( + #mont_reduce_self_params + ); + + (self.0).0[0] & rhs + } + } + + impl ::core::ops::Shr for #name { + type Output = #name; + + #[inline(always)] + fn shr(mut self, mut n: u32) -> #name { + if n as usize >= 64 * #limbs { + return Self::from(0); + } + + // Convert from Montgomery to native representation. + self.mont_reduce( + #mont_reduce_self_params + ); + + while n >= 64 { + let mut t = 0; + for i in (self.0).0.iter_mut().rev() { + ::core::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in (self.0).0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + + // Convert back to Montgomery representation + self * #name(R2) + } + } + impl ::ff::PrimeField for #name { type Repr = #repr; @@ -1023,7 +1078,7 @@ fn prime_field_impl( fn into_repr(&self) -> #repr { let mut r = *self; r.mont_reduce( - #into_repr_params + #mont_reduce_self_params ); r.0 diff --git a/ff/src/lib.rs b/ff/src/lib.rs index b2f4d3af65..78fbc5c37c 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -257,7 +257,9 @@ impl fmt::Display for PrimeFieldDecodingError { } /// This represents an element of a prime field. -pub trait PrimeField: Field + From { +pub trait PrimeField: + Field + From + BitAnd + Shr +{ /// The prime field can be converted back and forth into this biginteger /// representation. type Repr: PrimeFieldRepr + From; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 8e5d660286..f9caf5e95e 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1931,6 +1931,79 @@ fn test_fq_mul_assign() { } } +#[test] +fn test_fq_shr() { + let mut a = Fq::from_repr(FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e, + ])) + .unwrap(); + a = a >> 0; + assert_eq!( + a.into_repr(), + FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e, + ]) + ); + a = a >> 1; + assert_eq!( + a.into_repr(), + FqRepr([ + 0xd52e6eb0b9423ffe, + 0x21921603576aa943, + 0xceeead98979ee882, + 0xe2aa0fea40235bf3, + 0x0b04067a038f0582, + 0x0912f9480d7528a7, + ]) + ); + a = a >> 50; + assert_eq!( + a.into_repr(), + FqRepr([ + 0x8580d5daaa50f54b, + 0xab6625e7ba208864, + 0x83fa9008d6fcf3bb, + 0x019e80e3c160b8aa, + 0xbe52035d4a29c2c1, + 0x0000000000000244, + ]) + ); + a = a >> 130; + assert_eq!( + a.into_repr(), + FqRepr([ + 0xa0fea40235bf3cee, + 0x4067a038f0582e2a, + 0x2f9480d7528a70b0, + 0x0000000000000091, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); + a = a >> 64; + assert_eq!( + a.into_repr(), + FqRepr([ + 0x4067a038f0582e2a, + 0x2f9480d7528a70b0, + 0x0000000000000091, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); +} + #[test] fn test_fq_squaring() { let a = Fq(FqRepr([ diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index cc02d220e9..6bfb17520f 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -669,6 +669,67 @@ fn test_fr_mul_assign() { } } +#[test] +fn test_fr_shr() { + let mut a = Fr::from_repr(FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1, + ])) + .unwrap(); + a = a >> 0; + assert_eq!( + a.into_repr(), + FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1, + ]) + ); + a = a >> 1; + assert_eq!( + a.into_repr(), + FrRepr([ + 0xd99fdd762415141f, + 0xccbef069d44659ef, + 0xcd7b16954d072a92, + 0x1b001d5846f386d0, + ]) + ); + a = a >> 50; + assert_eq!( + a.into_repr(), + FrRepr([ + 0xbc1a7511967bf667, + 0xc5a55341caa4b32f, + 0x075611bce1b4335e, + 0x00000000000006c0, + ]) + ); + a = a >> 130; + assert_eq!( + a.into_repr(), + FrRepr([ + 0x01d5846f386d0cd7, + 0x00000000000001b0, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); + a = a >> 64; + assert_eq!( + a.into_repr(), + FrRepr([ + 0x00000000000001b0, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); +} + #[test] fn test_fr_squaring() { let a = Fr(FrRepr([ diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index e4bea138d3..81d7089b40 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -3,7 +3,8 @@ use ff::{ PrimeFieldRepr, SqrtField, }; use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::mem; +use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::ToUniform; @@ -452,6 +453,69 @@ impl MulAssign for Fs { } } +impl BitAnd for Fs { + type Output = u64; + + #[inline(always)] + fn bitand(mut self, rhs: u64) -> u64 { + self.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + 0, + 0, + 0, + 0, + ); + (self.0).0[0] & rhs + } +} + +impl Shr for Fs { + type Output = Self; + + #[inline(always)] + fn shr(mut self, mut n: u32) -> Self { + if n as usize >= 64 * 4 { + return Self::from(0); + } + + // Convert from Montgomery to native representation. + self.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + 0, + 0, + 0, + 0, + ); + + while n >= 64 { + let mut t = 0; + for i in (self.0).0.iter_mut().rev() { + mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in (self.0).0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + + // Convert back to Montgomery representation + self * Fs(R2) + } +} + impl PrimeField for Fs { type Repr = FsRepr; @@ -1400,6 +1464,67 @@ fn test_fs_mul_assign() { } } +#[test] +fn test_fs_shr() { + let mut a = Fs::from_repr(FsRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x06003ab08de70da1, + ])) + .unwrap(); + a = a >> 0; + assert_eq!( + a.into_repr(), + FsRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x06003ab08de70da1, + ]) + ); + a = a >> 1; + assert_eq!( + a.into_repr(), + FsRepr([ + 0xd99fdd762415141f, + 0xccbef069d44659ef, + 0xcd7b16954d072a92, + 0x03001d5846f386d0, + ]) + ); + a = a >> 50; + assert_eq!( + a.into_repr(), + FsRepr([ + 0xbc1a7511967bf667, + 0xc5a55341caa4b32f, + 0x075611bce1b4335e, + 0x00000000000000c0, + ]) + ); + a = a >> 130; + assert_eq!( + a.into_repr(), + FsRepr([ + 0x01d5846f386d0cd7, + 0x0000000000000030, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); + a = a >> 64; + assert_eq!( + a.into_repr(), + FsRepr([ + 0x0000000000000030, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]) + ); +} + #[test] fn test_fr_squaring() { let a = Fs(FsRepr([ diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index ea182a5ac1..afd8a7325f 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -1,7 +1,7 @@ //! Implementation of the Pedersen hash function used in Sapling. use crate::jubjub::*; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::Field; use std::ops::{AddAssign, Neg}; #[derive(Copy, Clone)] @@ -88,16 +88,14 @@ where 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; + let i = (acc & window_mask) as usize; tmp = tmp.add(&table[0][i], params); - acc.shr(window); + acc = acc >> window; table = &table[1..]; } From 1a40cfd39c0db51313c2de50c344e1bdd905c9a6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Apr 2020 18:58:36 +1200 Subject: [PATCH 010/210] zcash_primitives: Make jubjub::Fs::invert constant time --- zcash_primitives/src/jubjub/fs.rs | 68 +++++++------------------------ 1 file changed, 15 insertions(+), 53 deletions(-) diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 81d7089b40..f3af2b1ceb 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -617,61 +617,23 @@ impl Field for Fs { ret } - /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! - /// THIS WILL BE REPLACED BY THE jubjub CRATE, WHICH IS CONSTANT TIME! fn invert(&self) -> CtOption { - if self.is_zero() { - CtOption::new(Self::zero(), Choice::from(0)) - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - let one = FsRepr::from(1); - - let mut u = self.0; - let mut v = MODULUS; - let mut b = Fs(R2); // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != one && v != one { - while u.is_even() { - u.div2(); - - if b.0.is_even() { - b.0.div2(); - } else { - b.0.add_nocarry(&MODULUS); - b.0.div2(); - } - } - - while v.is_even() { - v.div2(); - - if c.0.is_even() { - c.0.div2(); - } else { - c.0.add_nocarry(&MODULUS); - c.0.div2(); - } - } - - if v < u { - u.sub_noborrow(&v); - b.sub_assign(&c); - } else { - v.sub_noborrow(&u); - c.sub_assign(&b); - } - } + // We need to find b such that b * a ≡ 1 mod p. As we are in a prime + // field, we can apply Fermat's Little Theorem: + // + // a^p ≡ a mod p + // a^(p-1) ≡ 1 mod p + // a^(p-2) * a ≡ 1 mod p + // + // Thus inversion can be implemented with a single exponentiation. + let inverse = self.pow_vartime(&[ + 0xd097_0e5e_d6f7_2cb5u64, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]); - if u == one { - CtOption::new(b, Choice::from(1)) - } else { - CtOption::new(c, Choice::from(1)) - } - } + CtOption::new(inverse, Choice::from(if self.is_zero() { 0 } else { 1 })) } #[inline(always)] From 1fe3e3784cf9c9f1ec4da0210c6095b56fb3447a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Apr 2020 16:30:36 +1200 Subject: [PATCH 011/210] ff: Add Ord bound to PrimeField --- bellman/src/groth16/tests/dummy_engine.rs | 12 ++++++++++++ ff/src/lib.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 14 +++++++++++++ zcash_primitives/src/jubjub/tests.rs | 24 +++++++++-------------- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 86e7b182eb..6d3ae73f0a 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -50,6 +50,18 @@ impl ConditionallySelectable for Fr { } } +impl Ord for Fr { + fn cmp(&self, other: &Fr) -> Ordering { + (self.0).0.cmp(&(other.0).0) + } +} + +impl PartialOrd for Fr { + fn partial_cmp(&self, other: &Fr) -> Option { + Some(self.cmp(other)) + } +} + impl Neg for Fr { type Output = Self; diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 78fbc5c37c..9a4028be9e 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -258,7 +258,7 @@ impl fmt::Display for PrimeFieldDecodingError { /// This represents an element of a prime field. pub trait PrimeField: - Field + From + BitAnd + Shr + Field + Ord + From + BitAnd + Shr { /// The prime field can be converted back and forth into this biginteger /// representation. diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index f3af2b1ceb..85c3df4ce1 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -272,6 +272,20 @@ impl ConstantTimeEq for Fs { } } +impl Ord for Fs { + #[inline(always)] + fn cmp(&self, other: &Fs) -> ::std::cmp::Ordering { + self.into_repr().cmp(&other.into_repr()) + } +} + +impl PartialOrd for Fs { + #[inline(always)] + fn partial_cmp(&self, other: &Fs) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + impl ::std::fmt::Display for Fs { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fs({})", self.into_repr()) diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index 6f66c44466..fca26b9160 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -1,6 +1,6 @@ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use rand_core::{RngCore, SeedableRng}; @@ -370,32 +370,26 @@ fn test_jubjub_params(params: &E::Params) { // 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 cur = E::Fs::one(); - let mut max = E::Fs::char(); - { - max.sub_noborrow(&E::Fs::one().into_repr()); - max.div2(); - } + let max = (-E::Fs::one()) >> 1; - let mut pacc = E::Fs::zero().into_repr(); - let mut nacc = E::Fs::char(); + let mut pacc = E::Fs::zero(); + let mut nacc = E::Fs::zero(); for _ in 0..params.pedersen_hash_chunks_per_generator() { // tmp = cur * 4 - let mut tmp = cur; - tmp.mul2(); - tmp.mul2(); + let tmp = cur.double().double(); - pacc.add_nocarry(&tmp); - nacc.sub_noborrow(&tmp); + pacc += &tmp; + nacc -= &tmp; // The first subtraction wraps intentionally. assert!(pacc < max); assert!(pacc < nacc); // cur = cur * 16 for _ in 0..4 { - cur.mul2(); + cur = cur.double(); } } } From 49f119fb033dac88165028b7117e3d23e9f469a8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Apr 2020 17:32:04 +1200 Subject: [PATCH 012/210] ff: Remove PrimeFieldRepr trait The ff::PrimeField::Repr associated type now has the minimal necessary bounds, which can be satisfied by a newtype around a byte array. --- bellman/src/gadgets/boolean.rs | 4 +- bellman/src/gadgets/num.rs | 13 +- bellman/src/gadgets/test/mod.rs | 9 +- bellman/src/groth16/prover.rs | 20 +- bellman/src/groth16/tests/dummy_engine.rs | 90 +- bellman/src/multiexp.rs | 22 +- ff/ff_derive/src/lib.rs | 642 ++++++---- ff/src/lib.rs | 148 +-- group/src/lib.rs | 10 +- group/src/wnaf.rs | 13 +- pairing/benches/bls12_381/fq.rs | 138 +-- pairing/benches/bls12_381/fr.rs | 138 +-- pairing/src/bls12_381/ec.rs | 697 +++++------ pairing/src/bls12_381/fq.rs | 866 ++++---------- pairing/src/bls12_381/fq2.rs | 508 ++++---- pairing/src/bls12_381/fr.rs | 607 ++-------- pairing/src/bls12_381/tests/mod.rs | 84 +- pairing/src/tests/field.rs | 2 +- pairing/src/tests/repr.rs | 87 +- zcash_client_backend/src/proto/mod.rs | 6 +- zcash_client_backend/src/welding_rig.rs | 9 +- zcash_primitives/src/jubjub/edwards.rs | 22 +- zcash_primitives/src/jubjub/fs.rs | 1044 +++++------------ zcash_primitives/src/jubjub/montgomery.rs | 2 +- zcash_primitives/src/keys.rs | 14 +- zcash_primitives/src/merkle_tree.rs | 6 +- zcash_primitives/src/note_encryption.rs | 55 +- zcash_primitives/src/primitives.rs | 4 +- zcash_primitives/src/redjubjub.rs | 12 +- zcash_primitives/src/sapling.rs | 12 +- .../src/transaction/components.rs | 20 +- zcash_primitives/src/transaction/sighash.rs | 4 +- zcash_primitives/src/zip32.rs | 17 +- zcash_proofs/src/circuit/ecc.rs | 4 +- zcash_proofs/src/circuit/sapling.rs | 8 +- 35 files changed, 1704 insertions(+), 3633 deletions(-) diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index f11768164e..2ccad51a7f 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -313,12 +313,12 @@ pub fn field_into_allocated_bits_le, F: // Deconstruct in big-endian bit order let values = match value { Some(ref value) => { - let mut field_char = BitIterator::::new(F::char()); + let mut field_char = BitIterator::::new(F::char()); let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); let mut found_one = false; - for b in BitIterator::::new(value.into_repr()) { + for b in BitIterator::::new(value.into_repr()) { // Skip leading bits found_one |= field_char.next().unwrap(); if !found_one { diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index f8ce6d32dc..8f736636d4 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -1,6 +1,6 @@ //! Gadgets representing numbers in the scalar field of the underlying curve. -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use ff::{BitIterator, Field, PrimeField, ScalarEngine}; use std::ops::{AddAssign, MulAssign}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; @@ -103,11 +103,8 @@ impl AllocatedNum { // We want to ensure that the bit representation of a is // less than or equal to r - 1. - let mut a = self - .value - .map(|e| BitIterator::::new(e.into_repr())); - let mut b = E::Fr::char(); - b.sub_noborrow(&1.into()); + let mut a = self.value.map(|e| BitIterator::::new(e.into_repr())); + let b = (-E::Fr::one()).into_repr(); let mut result = vec![]; @@ -117,7 +114,7 @@ impl AllocatedNum { let mut found_one = false; let mut i = 0; - for b in BitIterator::::new(b) { + for b in BitIterator::::new(b) { let a_bit = a.as_mut().map(|e| e.next().unwrap()); // Skip over unset bits at the beginning @@ -560,7 +557,7 @@ mod test { assert!(cs.is_satisfied()); - for (b, a) in BitIterator::::new(r.into_repr()) + for (b, a) in BitIterator::::new(r.into_repr()) .skip(1) .zip(bits.iter().rev()) { diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index a803accaef..b082907492 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,6 +1,6 @@ //! Helpers for testing circuit implementations. -use ff::{Field, PowVartime, PrimeField, PrimeFieldRepr, ScalarEngine}; +use ff::{Field, PowVartime, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; @@ -106,7 +106,12 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } } - coeff.into_repr().write_be(&mut buf[9..]).unwrap(); + // BLS12-381's Fr is canonically serialized in little-endian, but the hasher + // writes its coefficients in big endian. For now, we flip the endianness + // manually, which is not necessarily correct for circuits using other curves. + // TODO: Fix this in a standalone commit, and document the no-op change. + let coeff_be: Vec<_> = coeff.into_repr().as_ref().iter().cloned().rev().collect(); + buf[9..].copy_from_slice(&coeff_be[..]); h.update(&buf); } diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index c31b4db97a..34abbb4ea9 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use futures::Future; -use ff::{Field, PrimeField}; +use ff::Field; use group::{CurveAffine, CurveProjective}; use pairing::Engine; @@ -229,26 +229,14 @@ where let a_len = a.len() - 1; a.truncate(a_len); // TODO: parallelize if it's even helpful - let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); + let a = Arc::new(a.into_iter().map(|s| s.0).collect::>()); multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; // TODO: parallelize if it's even helpful - let input_assignment = Arc::new( - prover - .input_assignment - .into_iter() - .map(|s| s.into_repr()) - .collect::>(), - ); - let aux_assignment = Arc::new( - prover - .aux_assignment - .into_iter() - .map(|s| s.into_repr()) - .collect::>(), - ); + let input_assignment = Arc::new(prover.input_assignment); + let aux_assignment = Arc::new(prover.aux_assignment); let l = multiexp( &worker, diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 6d3ae73f0a..69937ce9c6 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,6 +1,4 @@ -use ff::{ - Field, PowVartime, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField, -}; +use ff::{Field, PowVartime, PrimeField, ScalarEngine, SqrtField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; @@ -259,86 +257,35 @@ impl SqrtField for Fr { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FrRepr([u64; 1]); +pub struct FrRepr([u8; 8]); -impl Ord for FrRepr { - fn cmp(&self, other: &FrRepr) -> Ordering { - (self.0)[0].cmp(&(other.0)[0]) - } -} - -impl PartialOrd for FrRepr { - fn partial_cmp(&self, other: &FrRepr) -> Option { - Some(self.cmp(other)) - } -} - -impl fmt::Display for FrRepr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", (self.0)[0]) - } -} - -impl From for FrRepr { - fn from(v: u64) -> FrRepr { - FrRepr([v]) +impl From for FrRepr { + fn from(v: Fr) -> FrRepr { + FrRepr::from(&v) } } -impl From for FrRepr { - fn from(v: Fr) -> FrRepr { - FrRepr([(v.0).0 as u64]) +impl<'a> From<&'a Fr> for FrRepr { + fn from(v: &'a Fr) -> FrRepr { + FrRepr(((v.0).0 as u64).to_le_bytes()) } } -impl AsMut<[u64]> for FrRepr { - fn as_mut(&mut self) -> &mut [u64] { +impl AsMut<[u8]> for FrRepr { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0[..] } } -impl AsRef<[u64]> for FrRepr { - fn as_ref(&self) -> &[u64] { +impl AsRef<[u8]> for FrRepr { + fn as_ref(&self) -> &[u8] { &self.0[..] } } impl Default for FrRepr { fn default() -> FrRepr { - FrRepr::from(0u64) - } -} - -impl PrimeFieldRepr for FrRepr { - fn sub_noborrow(&mut self, other: &Self) { - self.0[0] = self.0[0].wrapping_sub(other.0[0]); - } - fn add_nocarry(&mut self, other: &Self) { - self.0[0] = self.0[0].wrapping_add(other.0[0]); - } - fn num_bits(&self) -> u32 { - 64 - self.0[0].leading_zeros() - } - fn is_zero(&self) -> bool { - self.0[0] == 0 - } - fn is_odd(&self) -> bool { - !self.is_even() - } - fn is_even(&self) -> bool { - self.0[0] % 2 == 0 - } - fn div2(&mut self) { - self.shr(1) - } - fn shr(&mut self, amt: u32) { - self.0[0] >>= amt; - } - fn mul2(&mut self) { - self.shl(1) - } - fn shl(&mut self, amt: u32) { - self.0[0] <<= amt; + FrRepr([0; 8]) } } @@ -349,11 +296,12 @@ impl PrimeField for Fr { const CAPACITY: u32 = 15; const S: u32 = 10; - fn from_repr(repr: FrRepr) -> Result { - if repr.0[0] >= (MODULUS_R.0 as u64) { - Err(PrimeFieldDecodingError::NotInField) + fn from_repr(repr: FrRepr) -> Option { + let v = u64::from_le_bytes(repr.0); + if v >= (MODULUS_R.0 as u64) { + None } else { - Ok(Fr(Wrapping(repr.0[0] as u32))) + Some(Fr(Wrapping(v as u32))) } } @@ -464,7 +412,7 @@ impl CurveProjective for Fr { *self } - fn recommended_wnaf_for_scalar(_: &::Repr) -> usize { + fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { 3 } diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 0bc61ba148..18f48bd46f 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -1,6 +1,6 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use ff::{Field, PrimeField, ScalarEngine}; use futures::Future; use group::{CurveAffine, CurveProjective}; use std::io; @@ -154,7 +154,7 @@ fn multiexp_inner( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, + exponents: Arc::Fr>>, mut skip: u32, c: u32, handle_trivial: bool, @@ -181,13 +181,12 @@ where // Create space for the buckets let mut buckets = vec![G::zero(); (1 << c) - 1]; - let zero = ::Fr::zero().into_repr(); - let one = ::Fr::one().into_repr(); + let one = ::Fr::one(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { if density { - if exp == zero { + if exp.is_zero() { bases.skip(1)?; } else if exp == one { if handle_trivial { @@ -196,9 +195,8 @@ where bases.skip(1)?; } } else { - let mut exp = exp; - exp.shr(skip); - let exp = exp.as_ref()[0] % (1 << c); + let exp = exp >> skip; + let exp = exp & ((1 << c) - 1); if exp != 0 { (&mut buckets[(exp - 1) as usize]) @@ -261,7 +259,7 @@ pub fn multiexp( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, + exponents: Arc::Fr>>, ) -> Box> where for<'a> &'a Q: QueryDensity, @@ -290,14 +288,14 @@ where fn test_with_bls12() { fn naive_multiexp( bases: Arc::Affine>>, - exponents: Arc::Repr>>, + exponents: Arc>, ) -> G { assert_eq!(bases.len(), exponents.len()); let mut acc = G::zero(); for (base, exp) in bases.iter().zip(exponents.iter()) { - AddAssign::<&G>::add_assign(&mut acc, &base.mul(*exp)); + AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.into_repr())); } acc @@ -311,7 +309,7 @@ fn test_with_bls12() { let rng = &mut rand::thread_rng(); let v = Arc::new( (0..SAMPLES) - .map(|_| ::Fr::random(rng).into_repr()) + .map(|_| ::Fr::random(rng)) .collect::>(), ); let g = Arc::new( diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index f5439c5357..0a7db91a81 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -8,19 +8,105 @@ use num_integer::Integer; use num_traits::{One, ToPrimitive, Zero}; use quote::quote; use quote::TokenStreamExt; +use std::iter; use std::str::FromStr; mod pow_fixed; -#[proc_macro_derive(PrimeField, attributes(PrimeFieldModulus, PrimeFieldGenerator))] +enum ReprEndianness { + Big, + Little, +} + +impl FromStr for ReprEndianness { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "big" => Ok(ReprEndianness::Big), + "little" => Ok(ReprEndianness::Little), + _ => Err(()), + } + } +} + +impl ReprEndianness { + fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { + let read_repr = match self { + ReprEndianness::Big => quote! { + ::byteorder::BigEndian::read_u64_into(r.as_ref(), &mut inner[..]); + inner.reverse(); + }, + ReprEndianness::Little => quote! { + ::byteorder::LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); + }, + }; + + quote! { + use ::byteorder::ByteOrder; + + let r = { + let mut inner = [0u64; #limbs]; + #read_repr + #name(inner) + }; + + if r.is_valid() { + Some(r * R2) + } else { + None + } + } + } + + fn into_repr( + &self, + repr: &syn::Ident, + mont_reduce_self_params: &proc_macro2::TokenStream, + limbs: usize, + ) -> proc_macro2::TokenStream { + let bytes = limbs * 8; + + let write_repr = match self { + ReprEndianness::Big => quote! { + r.0.reverse(); + ::byteorder::BigEndian::write_u64_into(&r.0, &mut repr[..]); + }, + ReprEndianness::Little => quote! { + ::byteorder::LittleEndian::write_u64_into(&r.0, &mut repr[..]); + }, + }; + + quote! { + use ::byteorder::ByteOrder; + + let mut r = *self; + r.mont_reduce( + #mont_reduce_self_params + ); + + let mut repr = [0u8; #bytes]; + #write_repr + #repr(repr) + } + } + + fn iter_be(&self) -> proc_macro2::TokenStream { + match self { + ReprEndianness::Big => quote! {self.0.iter()}, + ReprEndianness::Little => quote! {self.0.iter().rev()}, + } + } +} + +#[proc_macro_derive( + PrimeField, + attributes(PrimeFieldModulus, PrimeFieldGenerator, PrimeFieldReprEndianness) +)] pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Parse the type definition let ast: syn::DeriveInput = syn::parse(input).unwrap(); - // The struct we're deriving for is a wrapper around a "Repr" type we must construct. - let repr_ident = fetch_wrapped_ident(&ast.data) - .expect("PrimeField derive only operates over tuple structs of a single item"); - // We're given the modulus p of the prime field let modulus: BigUint = fetch_attr("PrimeFieldModulus", &ast.attrs) .expect("Please supply a PrimeFieldModulus attribute") @@ -29,11 +115,18 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // We may be provided with a generator of p - 1 order. It is required that this generator be quadratic // nonresidue. + // TODO: Compute this ourselves. let generator: BigUint = fetch_attr("PrimeFieldGenerator", &ast.attrs) .expect("Please supply a PrimeFieldGenerator attribute") .parse() .expect("PrimeFieldGenerator should be a number"); + // Field element representations may be in little-endian or big-endian. + let endianness = fetch_attr("PrimeFieldReprEndianness", &ast.attrs) + .expect("Please supply a PrimeFieldReprEndianness attribute") + .parse() + .expect("PrimeFieldReprEndianness should be 'big' or 'little'"); + // The arithmetic in this library only works if the modulus*2 is smaller than the backing // representation. Compute the number of limbs we need. let mut limbs = 1; @@ -46,34 +139,146 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { } } + // The struct we're deriving for must be a wrapper around `pub [u64; limbs]`. + if let Some(err) = validate_struct(&ast, limbs) { + return err.into(); + } + + // Generate the identifier for the "Repr" type we must construct. + let repr_ident = syn::Ident::new( + &format!("{}Repr", ast.ident), + proc_macro2::Span::call_site(), + ); + let mut gen = proc_macro2::TokenStream::new(); let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt(&ast.ident, &repr_ident, &modulus, limbs, generator); gen.extend(constants_impl); - gen.extend(prime_field_repr_impl(&repr_ident, limbs)); - gen.extend(prime_field_impl(&ast.ident, &repr_ident, &modulus, limbs)); + gen.extend(prime_field_repr_impl(&repr_ident, &endianness, limbs * 8)); + gen.extend(prime_field_impl( + &ast.ident, + &repr_ident, + &modulus, + &endianness, + limbs, + )); gen.extend(sqrt_impl); // Return the generated impl gen.into() } -/// Fetches the ident being wrapped by the type we're deriving. -fn fetch_wrapped_ident(body: &syn::Data) -> Option { - if let syn::Data::Struct(ref variant_data) = body { - if let syn::Fields::Unnamed(ref fields) = variant_data.fields { - if fields.unnamed.len() == 1 { - if let syn::Type::Path(ref path) = fields.unnamed[0].ty { - if path.path.segments.len() == 1 { - return Some(path.path.segments[0].ident.clone()); - } - } - } +/// Checks that `body` contains `pub [u64; limbs]`. +fn validate_struct(ast: &syn::DeriveInput, limbs: usize) -> Option { + // The body should be a struct. + let variant_data = match &ast.data { + syn::Data::Struct(x) => x, + _ => { + return Some( + syn::Error::new_spanned(ast, "PrimeField derive only works for structs.") + .to_compile_error(), + ) + } + }; + + // The struct should contain a single unnamed field. + let fields = match &variant_data.fields { + syn::Fields::Unnamed(x) if x.unnamed.len() == 1 => x, + _ => { + return Some( + syn::Error::new_spanned( + &ast.ident, + format!( + "The struct must contain an array of limbs. Change this to `{}([u64; {}])`", + ast.ident, limbs, + ), + ) + .to_compile_error(), + ) + } + }; + let field = &fields.unnamed[0]; + + // The field should be an array. + let arr = match &field.ty { + syn::Type::Array(x) => x, + _ => { + return Some( + syn::Error::new_spanned( + field, + format!( + "The inner field must be an array of limbs. Change this to `[u64; {}]`", + limbs, + ), + ) + .to_compile_error(), + ) } }; + // The array's element type should be `u64`. + if match arr.elem.as_ref() { + syn::Type::Path(path) => path + .path + .get_ident() + .map(|x| x.to_string() != "u64") + .unwrap_or(true), + _ => true, + } { + return Some( + syn::Error::new_spanned( + arr, + format!( + "PrimeField derive requires 64-bit limbs. Change this to `[u64; {}]", + limbs + ), + ) + .to_compile_error(), + ); + } + + // The array's length should be a literal int equal to `limbs`. + let lit_int = match match &arr.len { + syn::Expr::Lit(expr_lit) => match &expr_lit.lit { + syn::Lit::Int(lit_int) => Some(lit_int), + _ => None, + }, + _ => None, + } { + Some(x) => x, + _ => { + return Some( + syn::Error::new_spanned( + arr, + format!("To derive PrimeField, change this to `[u64; {}]`.", limbs), + ) + .to_compile_error(), + ) + } + }; + if lit_int.base10_digits() != limbs.to_string() { + return Some( + syn::Error::new_spanned( + lit_int, + format!("The given modulus requires {} limbs.", limbs), + ) + .to_compile_error(), + ); + } + + // The field should not be public. + match &field.vis { + syn::Visibility::Inherited => (), + _ => { + return Some( + syn::Error::new_spanned(&field.vis, "Field must not be public.").to_compile_error(), + ) + } + } + + // Valid! None } @@ -102,18 +307,49 @@ fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { None } -// Implement PrimeFieldRepr for the wrapped ident `repr` with `limbs` limbs. -fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { +// Implement the wrapped ident `repr` with `bytes` bytes. +fn prime_field_repr_impl( + repr: &syn::Ident, + endianness: &ReprEndianness, + bytes: usize, +) -> proc_macro2::TokenStream { + let repr_iter_be = endianness.iter_be(); + quote! { - #[derive(Copy, Clone, PartialEq, Eq, Default)] - pub struct #repr(pub [u64; #limbs]); + #[derive(Copy, Clone)] + pub struct #repr(pub [u8; #bytes]); + + impl ::subtle::ConstantTimeEq for #repr { + fn ct_eq(&self, other: &#repr) -> ::subtle::Choice { + self.0 + .iter() + .zip(other.0.iter()) + .map(|(a, b)| a.ct_eq(b)) + .fold(1.into(), |acc, x| acc & x) + } + } + + impl ::core::cmp::PartialEq for #repr { + fn eq(&self, other: &#repr) -> bool { + use ::subtle::ConstantTimeEq; + self.ct_eq(other).into() + } + } + + impl ::core::cmp::Eq for #repr { } + + impl ::core::default::Default for #repr { + fn default() -> #repr { + #repr([0u8; #bytes]) + } + } impl ::core::fmt::Debug for #repr { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; + for i in #repr_iter_be { + write!(f, "{:02x}", *i)?; } Ok(()) @@ -123,183 +359,27 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS impl ::core::fmt::Display for #repr { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; + for i in #repr_iter_be { + write!(f, "{:02x}", *i)?; } Ok(()) } } - impl AsRef<[u64]> for #repr { + impl AsRef<[u8]> for #repr { #[inline(always)] - fn as_ref(&self) -> &[u64] { + fn as_ref(&self) -> &[u8] { &self.0 } } - impl AsMut<[u64]> for #repr { + impl AsMut<[u8]> for #repr { #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } - - impl From for #repr { - #[inline(always)] - fn from(val: u64) -> #repr { - use core::default::Default; - - let mut repr = Self::default(); - repr.0[0] = val; - repr - } - } - - impl Ord for #repr { - #[inline(always)] - fn cmp(&self, other: &#repr) -> ::core::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::core::cmp::Ordering::Less - } else if a > b { - return ::core::cmp::Ordering::Greater - } - } - - ::core::cmp::Ordering::Equal - } - } - - impl PartialOrd for #repr { - #[inline(always)] - fn partial_cmp(&self, other: &#repr) -> Option<::core::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::ff::PrimeFieldRepr for #repr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (#limbs as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &#repr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &#repr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::sbb(*a, *b, &mut borrow); - } - } - } } } @@ -455,7 +535,7 @@ fn prime_field_constants_and_sqrt( let mut b = x * &w; // Initialize z as the 2^S root of unity. - let mut z = #name(ROOT_OF_UNITY); + let mut z = ROOT_OF_UNITY; for max_v in (1..=S).rev() { let mut k = 1; @@ -494,6 +574,11 @@ fn prime_field_constants_and_sqrt( let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); let r = biguint_to_u64_vec(r, limbs); + let modulus_repr = { + let mut buf = modulus.to_bytes_le(); + buf.extend(iter::repeat(0).take((limbs * 8) - buf.len())); + buf + }; let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 @@ -507,7 +592,10 @@ fn prime_field_constants_and_sqrt( ( quote! { /// This is the modulus m of the prime field - const MODULUS: #repr = #repr([#(#modulus,)*]); + const MODULUS: #repr = #repr([#(#modulus_repr,)*]); + + /// This is the modulus m of the prime field in limb form + const MODULUS_LIMBS: #name = #name([#(#modulus,)*]); /// The number of bits needed to represent the modulus. const MODULUS_BITS: u32 = #modulus_num_bits; @@ -517,23 +605,23 @@ fn prime_field_constants_and_sqrt( const REPR_SHAVE_BITS: u32 = #repr_shave_bits; /// 2^{limbs*64} mod m - const R: #repr = #repr(#r); + const R: #name = #name(#r); /// 2^{limbs*64*2} mod m - const R2: #repr = #repr(#r2); + const R2: #name = #name(#r2); /// -(m^{-1} mod m) mod m const INV: u64 = #inv; /// Multiplicative generator of `MODULUS` - 1 order, also quadratic /// nonresidue. - const GENERATOR: #repr = #repr(#generator); + const GENERATOR: #name = #name(#generator); /// 2^s * t = MODULUS - 1 with t odd const S: u32 = #s; /// 2^s root of unity computed by GENERATOR^t - const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); + const ROOT_OF_UNITY: #name = #name(#root_of_unity); }, sqrt_impl, ) @@ -544,6 +632,7 @@ fn prime_field_impl( name: &syn::Ident, repr: &syn::Ident, modulus: &BigUint, + endianness: &ReprEndianness, limbs: usize, ) -> proc_macro2::TokenStream { // Returns r{n} as an ident. @@ -575,14 +664,14 @@ fn prime_field_impl( gen.extend(quote! { let k = #temp.wrapping_mul(INV); let mut carry = 0; - ::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry); + ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[0], &mut carry); }); } for j in 1..limbs { let temp = get_temp(i + j); gen.extend(quote! { - #temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry); + #temp = ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[#j], &mut carry); }); } @@ -609,7 +698,7 @@ fn prime_field_impl( let temp = get_temp(limbs + i); gen.extend(quote! { - (self.0).0[#i] = #temp; + self.0[#i] = #temp; }); } @@ -628,11 +717,11 @@ fn prime_field_impl( let temp = get_temp(i + j); if i == 0 { gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry); + let #temp = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#j], &mut carry); }); } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#a.0).0[#j], &mut carry); + gen.extend(quote! { + let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #a.0[#j], &mut carry); }); } } @@ -672,11 +761,11 @@ fn prime_field_impl( let temp1 = get_temp(i * 2 + 1); if i == 0 { gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); + let #temp0 = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#i], &mut carry); }); } else { - gen.extend(quote!{ - let #temp0 = ::ff::mac_with_carry(#temp0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); + gen.extend(quote! { + let #temp0 = ::ff::mac_with_carry(#temp0, #a.0[#i], #a.0[#i], &mut carry); }); } @@ -717,11 +806,11 @@ fn prime_field_impl( if i == 0 { gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry); + let #temp = ::ff::mac_with_carry(0, #a.0[#i], #b.0[#j], &mut carry); }); } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#b.0).0[#j], &mut carry); + gen.extend(quote! { + let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #b.0[#j], &mut carry); }); } } @@ -778,10 +867,10 @@ fn prime_field_impl( let invert_impl = inv_impl(quote! {self}, name, modulus); let montgomery_impl = mont_impl(limbs); - // (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ... + // self.0[0].ct_eq(&other.0[0]) & self.0[1].ct_eq(&other.0[1]) & ... let mut ct_eq_impl = proc_macro2::TokenStream::new(); ct_eq_impl.append_separated( - (0..limbs).map(|i| quote! { (self.0).0[#i].ct_eq(&(other.0).0[#i]) }), + (0..limbs).map(|i| quote! { self.0[#i].ct_eq(&other.0[#i]) }), proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), ); @@ -790,7 +879,7 @@ fn prime_field_impl( let mut mont_reduce_params = proc_macro2::TokenStream::new(); mont_reduce_params.append_separated( (0..limbs) - .map(|i| quote! { (#a.0).0[#i] }) + .map(|i| quote! { #a.0[#i] }) .chain((0..limbs).map(|_| quote! {0})), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), ); @@ -798,6 +887,10 @@ fn prime_field_impl( } let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); + let mont_reduce_other_params = mont_reduce_params(quote! {other}, limbs); + + let from_repr_impl = endianness.from_repr(name, limbs); + let into_repr_impl = endianness.into_repr(repr, &mont_reduce_self_params, limbs); let top_limb_index = limbs - 1; @@ -818,13 +911,14 @@ fn prime_field_impl( impl ::subtle::ConstantTimeEq for #name { fn ct_eq(&self, other: &#name) -> ::subtle::Choice { - #ct_eq_impl + self.into_repr().ct_eq(&other.into_repr()) } } impl ::core::cmp::PartialEq for #name { fn eq(&self, other: &#name) -> bool { - self.0 == other.0 + use ::subtle::ConstantTimeEq; + self.ct_eq(other).into() } } @@ -841,7 +935,17 @@ fn prime_field_impl( impl Ord for #name { #[inline(always)] fn cmp(&self, other: &#name) -> ::core::cmp::Ordering { - self.into_repr().cmp(&other.into_repr()) + let mut a = *self; + a.mont_reduce( + #mont_reduce_self_params + ); + + let mut b = *other; + b.mont_reduce( + #mont_reduce_other_params + ); + + a.cmp_native(&b) } } @@ -863,7 +967,7 @@ fn prime_field_impl( fn from(val: u64) -> #name { let mut raw = [0u64; #limbs]; raw[0] = val; - #name(#repr(raw)) * #name(R2) + #name(raw) * R2 } } @@ -873,13 +977,19 @@ fn prime_field_impl( } } + impl<'a> From<&'a #name> for #repr { + fn from(e: &'a #name) -> #repr { + e.into_repr() + } + } + impl ::subtle::ConditionallySelectable for #name { fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name { let mut res = [0u64; #limbs]; for i in 0..#limbs { - res[i] = u64::conditional_select(&(a.0).0[i], &(b.0).0[i], choice); + res[i] = u64::conditional_select(&a.0[i], &b.0[i], choice); } - #name(#repr(res)) + #name(res) } } @@ -890,9 +1000,9 @@ fn prime_field_impl( fn neg(self) -> #name { let mut ret = self; if !ret.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&ret.0); - ret.0 = tmp; + let mut tmp = MODULUS_LIMBS; + tmp.sub_noborrow(&ret); + ret = tmp; } ret } @@ -922,7 +1032,7 @@ fn prime_field_impl( #[inline] fn add_assign(&mut self, other: &#name) { // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); + self.add_nocarry(other); // However, it may need to be reduced. self.reduce(); @@ -960,11 +1070,11 @@ fn prime_field_impl( #[inline] fn sub_assign(&mut self, other: &#name) { // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); + if other.cmp_native(self) == ::core::cmp::Ordering::Greater { + self.add_nocarry(&MODULUS_LIMBS); } - self.0.sub_noborrow(&other.0); + self.sub_noborrow(other); } } @@ -1020,7 +1130,7 @@ fn prime_field_impl( #mont_reduce_self_params ); - (self.0).0[0] & rhs + self.0[0] & rhs } } @@ -1040,7 +1150,7 @@ fn prime_field_impl( while n >= 64 { let mut t = 0; - for i in (self.0).0.iter_mut().rev() { + for i in self.0.iter_mut().rev() { ::core::mem::swap(&mut t, i); } n -= 64; @@ -1048,7 +1158,7 @@ fn prime_field_impl( if n > 0 { let mut t = 0; - for i in (self.0).0.iter_mut().rev() { + for i in self.0.iter_mut().rev() { let t2 = *i << (64 - n); *i >>= n; *i |= t; @@ -1057,39 +1167,32 @@ fn prime_field_impl( } // Convert back to Montgomery representation - self * #name(R2) + self * R2 } } impl ::ff::PrimeField for #name { type Repr = #repr; - fn from_repr(r: #repr) -> Result<#name, PrimeFieldDecodingError> { - let mut r = #name(r); - if r.is_valid() { - r.mul_assign(&#name(R2)); - - Ok(r) - } else { - Err(PrimeFieldDecodingError::NotInField) - } + fn from_repr(r: #repr) -> Option<#name> { + #from_repr_impl } fn into_repr(&self) -> #repr { + #into_repr_impl + } + + #[inline(always)] + fn is_odd(&self) -> bool { let mut r = *self; r.mont_reduce( #mont_reduce_self_params ); - r.0 - } - - #[inline(always)] - fn is_odd(&self) -> bool { - self.into_repr().is_odd() + r.0[0] & 1 == 1 } - fn char() -> #repr { + fn char() -> Self::Repr { MODULUS } @@ -1098,13 +1201,13 @@ fn prime_field_impl( const CAPACITY: u32 = Self::NUM_BITS - 1; fn multiplicative_generator() -> Self { - #name(GENERATOR) + GENERATOR } const S: u32 = S; fn root_of_unity() -> Self { - #name(ROOT_OF_UNITY) + ROOT_OF_UNITY } } @@ -1117,7 +1220,7 @@ fn prime_field_impl( for i in 0..#limbs { repr[i] = rng.next_u64(); } - #name(#repr(repr)) + #name(repr) }; // Mask away the unused most-significant bits. @@ -1131,17 +1234,17 @@ fn prime_field_impl( #[inline] fn zero() -> Self { - #name(#repr::from(0)) + #name([0; #limbs]) } #[inline] fn one() -> Self { - #name(R) + R } #[inline] fn is_zero(&self) -> bool { - self.0.is_zero() + self.0.iter().all(|&e| e == 0) } #[inline] @@ -1149,7 +1252,13 @@ fn prime_field_impl( let mut ret = *self; // This cannot exceed the backing capacity. - ret.0.mul2(); + let mut last = 0; + for i in &mut ret.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } // However, it may need to be reduced. ret.reduce(); @@ -1174,11 +1283,46 @@ fn prime_field_impl( } impl #name { + /// Compares two elements in native representation. This is only used + /// internally. + #[inline(always)] + fn cmp_native(&self, other: &#name) -> ::core::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::core::cmp::Ordering::Less + } else if a > b { + return ::core::cmp::Ordering::Greater + } + } + + ::core::cmp::Ordering::Equal + } + /// Determines if the element is really in the field. This is only used /// internally. #[inline(always)] fn is_valid(&self) -> bool { - self.0 < MODULUS + // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use + // this internal function to eliminate the cycle. + self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &#name) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::ff::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &#name) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::ff::sbb(*a, *b, &mut borrow); + } } /// Subtracts the modulus from this element if this element is not in the @@ -1186,7 +1330,7 @@ fn prime_field_impl( #[inline(always)] fn reduce(&mut self) { if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); + self.sub_noborrow(&MODULUS_LIMBS); } } diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 9a4028be9e..bb2994cdef 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -12,6 +12,7 @@ extern crate std; #[cfg(feature = "derive")] pub use ff_derive::*; +use core::convert::TryFrom; use core::fmt; use core::marker::PhantomData; use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; @@ -130,139 +131,13 @@ pub trait SqrtField: Field { fn sqrt(&self) -> CtOption; } -/// This trait represents a wrapper around a biginteger which can encode any element of a particular -/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit -/// first. -pub trait PrimeFieldRepr: - Sized - + Copy - + Clone - + Eq - + Ord - + Send - + Sync - + Default - + fmt::Debug - + fmt::Display - + 'static - + AsRef<[u64]> - + AsMut<[u64]> - + From -{ - /// Subtract another represetation from this one. - fn sub_noborrow(&mut self, other: &Self); - - /// Add another representation to this one. - fn add_nocarry(&mut self, other: &Self); - - /// Compute the number of bits needed to encode this number. Always a - /// multiple of 64. - fn num_bits(&self) -> u32; - - /// Returns true iff this number is zero. - fn is_zero(&self) -> bool; - - /// Returns true iff this number is odd. - fn is_odd(&self) -> bool; - - /// Returns true iff this number is even. - fn is_even(&self) -> bool; - - /// Performs a rightwise bitshift of this number, effectively dividing - /// it by 2. - fn div2(&mut self); - - /// Performs a rightwise bitshift of this number by some amount. - fn shr(&mut self, amt: u32); - - /// Performs a leftwise bitshift of this number, effectively multiplying - /// it by 2. Overflow is ignored. - fn mul2(&mut self); - - /// Performs a leftwise bitshift of this number by some amount. - fn shl(&mut self, amt: u32); - - /// Writes this `PrimeFieldRepr` as a big endian integer. - #[cfg(feature = "std")] - fn write_be(&self, mut writer: W) -> io::Result<()> { - use byteorder::{BigEndian, WriteBytesExt}; - - for digit in self.as_ref().iter().rev() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a big endian integer into this representation. - #[cfg(feature = "std")] - fn read_be(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{BigEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut().rev() { - *digit = reader.read_u64::()?; - } - - Ok(()) - } - - /// Writes this `PrimeFieldRepr` as a little endian integer. - #[cfg(feature = "std")] - fn write_le(&self, mut writer: W) -> io::Result<()> { - use byteorder::{LittleEndian, WriteBytesExt}; - - for digit in self.as_ref().iter() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a little endian integer into this representation. - #[cfg(feature = "std")] - fn read_le(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{LittleEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut() { - *digit = reader.read_u64::()?; - } - - Ok(()) - } -} - -/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a -/// `PrimeField` element. -#[derive(Debug)] -pub enum PrimeFieldDecodingError { - /// The encoded value is not in the field - NotInField, -} - -#[cfg(feature = "std")] -impl std::error::Error for PrimeFieldDecodingError { - fn description(&self) -> &str { - match *self { - PrimeFieldDecodingError::NotInField => "not an element of the field", - } - } -} - -impl fmt::Display for PrimeFieldDecodingError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - PrimeFieldDecodingError::NotInField => write!(f, "not an element of the field"), - } - } -} - /// This represents an element of a prime field. pub trait PrimeField: Field + Ord + From + BitAnd + Shr { - /// The prime field can be converted back and forth into this biginteger + /// The prime field can be converted back and forth into this binary /// representation. - type Repr: PrimeFieldRepr + From; + type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + for<'r> From<&'r Self>; /// Interpret a string of numbers as a (congruent) prime field element. /// Does not accept unnecessary leading zeroes or a blank string. @@ -304,11 +179,20 @@ pub trait PrimeField: Some(res) } - /// Convert this prime field element into a biginteger representation. - fn from_repr(_: Self::Repr) -> Result; + /// Attempts to convert a byte representation of a field element into an element of + /// this prime field, failing if the input is not canonical (is not smaller than the + /// field's modulus). + /// + /// The byte representation is interpreted with the same endianness as is returned + /// by [`PrimeField::into_repr`]. + fn from_repr(_: Self::Repr) -> Option; - /// Convert a biginteger representation into a prime field element, if - /// the number is an element of the field. + /// Converts an element of the prime field into the standard byte representation for + /// this field. + /// + /// Endianness of the byte representation is defined by the field implementation. + /// Callers should assume that it is the standard endianness used to represent encoded + /// elements of this particular field. fn into_repr(&self) -> Self::Repr; /// Returns true iff this element is odd. diff --git a/group/src/lib.rs b/group/src/lib.rs index 3dd9bbd217..89104945da 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -1,7 +1,7 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; +use ff::{PrimeField, ScalarEngine, SqrtField}; use rand::RngCore; use std::error::Error; use std::fmt; @@ -82,7 +82,7 @@ pub trait CurveProjective: /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. - fn recommended_wnaf_for_scalar(scalar: &::Repr) -> usize; + fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> usize; /// Recommends a wNAF window size given the number of scalars you intend to multiply /// a base by. Always returns a number between 2 and 22, inclusive. @@ -178,7 +178,7 @@ pub enum GroupDecodingError { /// The element is not part of the r-order subgroup. NotInSubgroup, /// One of the coordinates could not be decoded - CoordinateDecodingError(&'static str, PrimeFieldDecodingError), + CoordinateDecodingError(&'static str), /// The compression mode of the encoded element was not as expected UnexpectedCompressionMode, /// The encoding contained bits that should not have been set @@ -202,8 +202,8 @@ impl Error for GroupDecodingError { impl fmt::Display for GroupDecodingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - GroupDecodingError::CoordinateDecodingError(description, ref err) => { - write!(f, "{} decoding error: {}", description, err) + GroupDecodingError::CoordinateDecodingError(description) => { + write!(f, "{} decoding error", description) } _ => write!(f, "{}", self.description()), } diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 200110157a..261b301d2a 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -1,3 +1,4 @@ +use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; @@ -19,7 +20,7 @@ pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, wi /// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian /// scalar. -pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { +pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { // Required by the NAF definition debug_assert!(window >= 2); // Required so that the NAF digits fit in i64 @@ -27,11 +28,11 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usiz wnaf.truncate(0); - let u64_len = c.as_ref().len(); - let bit_len = u64_len * 64; + let bit_len = c.as_ref().len() * 8; + let u64_len = (bit_len + 1) / 64; let mut c_u64 = vec![0u64; u64_len + 1]; - c_u64[0..u64_len].copy_from_slice(c.as_ref()); + LittleEndian::read_u64_into(c.as_ref(), &mut c_u64[0..u64_len]); let width = 1u64 << window; let window_mask = width - 1; @@ -144,13 +145,11 @@ impl Wnaf<(), Vec, Vec> { &mut self, scalar: &::Scalar, ) -> Wnaf, &[i64]> { - let scalar = scalar.into_repr(); - // Compute the appropriate window size for the scalar. let window_size = G::recommended_wnaf_for_scalar(&scalar); // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar, window_size); + wnaf_form(&mut self.scalar, scalar.into_repr(), window_size); // Return a Wnaf object that mutably borrows the base storage location, but // immutably borrows the computed wNAF form scalar location. diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs index 244c161bcd..3c43fdd1f2 100644 --- a/pairing/benches/bls12_381/fq.rs +++ b/pairing/benches/bls12_381/fq.rs @@ -3,140 +3,9 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField, SqrtField}; use pairing::bls12_381::*; -fn bench_fq_repr_add_nocarry(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = Fq::random(&mut rng).into_repr(); - let mut tmp2 = Fq::random(&mut rng).into_repr(); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::add_nocarry", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_sub_noborrow(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = Fq::random(&mut rng).into_repr(); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::sub_noborrow", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_num_bits(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::num_bits", |b| { - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_mul2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::mul2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_div2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::div2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - fn bench_fq_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -328,11 +197,6 @@ fn bench_fq_from_repr(c: &mut Criterion) { criterion_group!( benches, - bench_fq_repr_add_nocarry, - bench_fq_repr_sub_noborrow, - bench_fq_repr_num_bits, - bench_fq_repr_mul2, - bench_fq_repr_div2, bench_fq_add_assign, bench_fq_sub_assign, bench_fq_mul_assign, diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs index d2dbc4c210..33b3901e1b 100644 --- a/pairing/benches/bls12_381/fr.rs +++ b/pairing/benches/bls12_381/fr.rs @@ -3,140 +3,9 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField, SqrtField}; use pairing::bls12_381::*; -fn bench_fr_repr_add_nocarry(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = Fr::random(&mut rng).into_repr(); - let mut tmp2 = Fr::random(&mut rng).into_repr(); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::add_nocarry", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_sub_noborrow(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = Fr::random(&mut rng).into_repr(); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::sub_noborrow", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_num_bits(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::num_bits", |b| { - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_mul2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::mul2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_div2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::div2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - fn bench_fr_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -328,11 +197,6 @@ fn bench_fr_from_repr(c: &mut Criterion) { criterion_group!( benches, - bench_fr_repr_add_nocarry, - bench_fr_repr_sub_noborrow, - bench_fr_repr_num_bits, - bench_fr_repr_mul2, - bench_fr_repr_div2, bench_fr_add_assign, bench_fr_sub_assign, bench_fr_mul_assign, diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 42bd91e20f..ef03797783 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -183,8 +183,8 @@ macro_rules! curve_impl { } fn mul::Repr>>(&self, by: S) -> $projective { - let bits = BitIterator::::new(by.into()); - self.mul_bits_u64(bits) + let bits = BitIterator::::new(by.into()); + self.mul_bits_u8(bits) } fn into_projective(&self) -> $projective { @@ -666,7 +666,7 @@ macro_rules! curve_impl { let mut found_one = false; - for i in BitIterator::::new(other.into()) { + for i in BitIterator::::new(other.into()) { if found_one { res.double(); } else { @@ -685,8 +685,10 @@ macro_rules! curve_impl { (*self).into() } - fn recommended_wnaf_for_scalar(scalar: &::Repr) -> usize { - Self::empirical_recommended_wnaf_for_scalar(scalar) + fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { + Self::empirical_recommended_wnaf_for_scalar( + ::NUM_BITS as usize, + ) } fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { @@ -749,10 +751,10 @@ macro_rules! curve_impl { } pub mod g1 { - use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; + use super::super::{Bls12, Fq, Fq12, FqRepr, Fr}; use super::g2::G2Affine; use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use ff::{BitIterator, Field, PrimeField, SqrtField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; @@ -842,22 +844,21 @@ pub mod g1 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x = FqRepr([0; 6]); - let mut y = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - y.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x = FqRepr(copy_segment(©, 0)); + let y = FqRepr(copy_segment(©, 48)); + Ok(G1Affine { - x: Fq::from_repr(x).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate", e) + x: Fq::from_repr(x).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate") })?, - y: Fq::from_repr(y).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate", e) + y: Fq::from_repr(y).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate") })?, infinity: false, }) @@ -871,10 +872,8 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - let mut writer = &mut res.0[..]; - - affine.x.into_repr().write_be(&mut writer).unwrap(); - affine.y.into_repr().write_be(&mut writer).unwrap(); + res.0[..48].copy_from_slice(&affine.x.into_repr().0); + res.0[48..].copy_from_slice(&affine.y.into_repr().0); } res @@ -950,17 +949,9 @@ pub mod g1 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - } - // Interpret as Fq element. - let x = Fq::from_repr(x) - .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; + let x = Fq::from_repr(FqRepr(copy)) + .ok_or_else(|| GroupDecodingError::CoordinateDecodingError("x coordinate"))?; let ret = G1Affine::get_point_from_x(x, greatest); if ret.is_some().into() { @@ -978,11 +969,7 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - { - let mut writer = &mut res.0[..]; - - affine.x.into_repr().write_be(&mut writer).unwrap(); - } + res.0 = affine.x.into_repr().0; let negy = affine.y.neg(); @@ -1025,9 +1012,7 @@ pub mod g1 { } impl G1 { - fn empirical_recommended_wnaf_for_scalar(scalar: &FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - + fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { if num_bits >= 130 { 4 } else if num_bits >= 34 { @@ -1082,13 +1067,11 @@ pub mod g1 { let y = rhs.sqrt(); if y.is_some().into() { let y = y.unwrap(); - let yrepr = y.into_repr(); let negy = y.neg(); - let negyrepr = negy.into_repr(); let p = G1Affine { x, - y: if yrepr < negyrepr { y } else { negy }, + y: if y < negy { y } else { negy }, infinity: false, }; assert!(!p.is_in_correct_subgroup_assuming_on_curve()); @@ -1116,21 +1099,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0xc58d887b66c035dc, - 0x10cbfd301d553822, - 0xaf23e064f1131ee5, - 0x9fe83b1b4a5d648d, - 0xf583cc5a508f6a40, - 0xc3ad2aefde0bb13, + 0x0c, 0x3a, 0xd2, 0xae, 0xfd, 0xe0, 0xbb, 0x13, 0xf5, 0x83, 0xcc, 0x5a, 0x50, + 0x8f, 0x6a, 0x40, 0x9f, 0xe8, 0x3b, 0x1b, 0x4a, 0x5d, 0x64, 0x8d, 0xaf, 0x23, + 0xe0, 0x64, 0xf1, 0x13, 0x1e, 0xe5, 0x10, 0xcb, 0xfd, 0x30, 0x1d, 0x55, 0x38, + 0x22, 0xc5, 0x8d, 0x88, 0x7b, 0x66, 0xc0, 0x35, 0xdc, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x60aa6f9552f03aae, - 0xecd01d5181300d35, - 0x8af1cdb8aa8ce167, - 0xe760f57922998c9d, - 0x953703f5795a39e5, - 0xfe3ae0922df702c, + 0x0f, 0xe3, 0xae, 0x09, 0x22, 0xdf, 0x70, 0x2c, 0x95, 0x37, 0x03, 0xf5, 0x79, + 0x5a, 0x39, 0xe5, 0xe7, 0x60, 0xf5, 0x79, 0x22, 0x99, 0x8c, 0x9d, 0x8a, 0xf1, + 0xcd, 0xb8, 0xaa, 0x8c, 0xe1, 0x67, 0xec, 0xd0, 0x1d, 0x51, 0x81, 0x30, 0x0d, + 0x35, 0x60, 0xaa, 0x6f, 0x95, 0x52, 0xf0, 0x3a, 0xae, ])) .unwrap(), infinity: false, @@ -1143,21 +1122,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0xee6adf83511e15f5, - 0x92ddd328f27a4ba6, - 0xe305bd1ac65adba7, - 0xea034ee2928b30a8, - 0xbd8833dc7c79a7f7, - 0xe45c9f0c0438675, + 0x0e, 0x45, 0xc9, 0xf0, 0xc0, 0x43, 0x86, 0x75, 0xbd, 0x88, 0x33, 0xdc, 0x7c, + 0x79, 0xa7, 0xf7, 0xea, 0x03, 0x4e, 0xe2, 0x92, 0x8b, 0x30, 0xa8, 0xe3, 0x05, + 0xbd, 0x1a, 0xc6, 0x5a, 0xdb, 0xa7, 0x92, 0xdd, 0xd3, 0x28, 0xf2, 0x7a, 0x4b, + 0xa6, 0xee, 0x6a, 0xdf, 0x83, 0x51, 0x1e, 0x15, 0xf5, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x3b450eb1ab7b5dad, - 0xa65cb81e975e8675, - 0xaa548682b21726e5, - 0x753ddf21a2601d20, - 0x532d0b640bd3ff8b, - 0x118d2c543f031102, + 0x11, 0x8d, 0x2c, 0x54, 0x3f, 0x03, 0x11, 0x02, 0x53, 0x2d, 0x0b, 0x64, 0x0b, + 0xd3, 0xff, 0x8b, 0x75, 0x3d, 0xdf, 0x21, 0xa2, 0x60, 0x1d, 0x20, 0xaa, 0x54, + 0x86, 0x82, 0xb2, 0x17, 0x26, 0xe5, 0xa6, 0x5c, 0xb8, 0x1e, 0x97, 0x5e, 0x86, + 0x75, 0x3b, 0x45, 0x0e, 0xb1, 0xab, 0x7b, 0x5d, 0xad, ])) .unwrap(), infinity: false, @@ -1171,21 +1146,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0x76e1c971c6db8fe8, - 0xe37e1a610eff2f79, - 0x88ae9c499f46f0c0, - 0xf35de9ce0d6b4e84, - 0x265bddd23d1dec54, - 0x12a8778088458308, + 0x12, 0xa8, 0x77, 0x80, 0x88, 0x45, 0x83, 0x08, 0x26, 0x5b, 0xdd, 0xd2, 0x3d, + 0x1d, 0xec, 0x54, 0xf3, 0x5d, 0xe9, 0xce, 0x0d, 0x6b, 0x4e, 0x84, 0x88, 0xae, + 0x9c, 0x49, 0x9f, 0x46, 0xf0, 0xc0, 0xe3, 0x7e, 0x1a, 0x61, 0x0e, 0xff, 0x2f, + 0x79, 0x76, 0xe1, 0xc9, 0x71, 0xc6, 0xdb, 0x8f, 0xe8, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x8a22defa0d526256, - 0xc57ca55456fcb9ae, - 0x1ba194e89bab2610, - 0x921beef89d4f29df, - 0x5b6fda44ad85fa78, - 0xed74ab9f302cbe0, + 0x0e, 0xd7, 0x4a, 0xb9, 0xf3, 0x02, 0xcb, 0xe0, 0x5b, 0x6f, 0xda, 0x44, 0xad, + 0x85, 0xfa, 0x78, 0x92, 0x1b, 0xee, 0xf8, 0x9d, 0x4f, 0x29, 0xdf, 0x1b, 0xa1, + 0x94, 0xe8, 0x9b, 0xab, 0x26, 0x10, 0xc5, 0x7c, 0xa5, 0x54, 0x56, 0xfc, 0xb9, + 0xae, 0x8a, 0x22, 0xde, 0xfa, 0x0d, 0x52, 0x62, 0x56, ])) .unwrap(), infinity: false, @@ -1199,21 +1170,17 @@ pub mod g1 { fn test_g1_addition_correctness() { let mut p = G1 { x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, + 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, + 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, + 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, + 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, + 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, + 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, + 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, + 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, ])) .unwrap(), z: Fq::one(), @@ -1221,21 +1188,17 @@ pub mod g1 { p.add_assign(&G1 { x: Fq::from_repr(FqRepr([ - 0xeec78f3096213cbf, - 0xa12beb1fea1056e6, - 0xc286c0211c40dd54, - 0x5f44314ec5e3fb03, - 0x24e8538737c6e675, - 0x8abd623a594fba8, + 0x08, 0xab, 0xd6, 0x23, 0xa5, 0x94, 0xfb, 0xa8, 0x24, 0xe8, 0x53, 0x87, 0x37, 0xc6, + 0xe6, 0x75, 0x5f, 0x44, 0x31, 0x4e, 0xc5, 0xe3, 0xfb, 0x03, 0xc2, 0x86, 0xc0, 0x21, + 0x1c, 0x40, 0xdd, 0x54, 0xa1, 0x2b, 0xeb, 0x1f, 0xea, 0x10, 0x56, 0xe6, 0xee, 0xc7, + 0x8f, 0x30, 0x96, 0x21, 0x3c, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x6b0528f088bb7044, - 0x2fdeb5c82917ff9e, - 0x9a5181f2fac226ad, - 0xd65104c6f95a872a, - 0x1f2998a5a9c61253, - 0xe74846154a9e44, + 0x00, 0xe7, 0x48, 0x46, 0x15, 0x4a, 0x9e, 0x44, 0x1f, 0x29, 0x98, 0xa5, 0xa9, 0xc6, + 0x12, 0x53, 0xd6, 0x51, 0x04, 0xc6, 0xf9, 0x5a, 0x87, 0x2a, 0x9a, 0x51, 0x81, 0xf2, + 0xfa, 0xc2, 0x26, 0xad, 0x2f, 0xde, 0xb5, 0xc8, 0x29, 0x17, 0xff, 0x9e, 0x6b, 0x05, + 0x28, 0xf0, 0x88, 0xbb, 0x70, 0x44, ])) .unwrap(), z: Fq::one(), @@ -1247,21 +1210,17 @@ pub mod g1 { p, G1Affine { x: Fq::from_repr(FqRepr([ - 0x6dd3098f22235df, - 0xe865d221c8090260, - 0xeb96bb99fa50779f, - 0xc4f9a52a428e23bb, - 0xd178b28dd4f407ef, - 0x17fb8905e9183c69 + 0x17, 0xfb, 0x89, 0x05, 0xe9, 0x18, 0x3c, 0x69, 0xd1, 0x78, 0xb2, 0x8d, 0xd4, + 0xf4, 0x07, 0xef, 0xc4, 0xf9, 0xa5, 0x2a, 0x42, 0x8e, 0x23, 0xbb, 0xeb, 0x96, + 0xbb, 0x99, 0xfa, 0x50, 0x77, 0x9f, 0xe8, 0x65, 0xd2, 0x21, 0xc8, 0x09, 0x02, + 0x60, 0x06, 0xdd, 0x30, 0x98, 0xf2, 0x22, 0x35, 0xdf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd0de9d65292b7710, - 0xf6a05f2bcf1d9ca7, - 0x1040e27012f20b64, - 0xeec8d1a5b7466c58, - 0x4bc362649dce6376, - 0x430cbdc5455b00a + 0x04, 0x30, 0xcb, 0xdc, 0x54, 0x55, 0xb0, 0x0a, 0x4b, 0xc3, 0x62, 0x64, 0x9d, + 0xce, 0x63, 0x76, 0xee, 0xc8, 0xd1, 0xa5, 0xb7, 0x46, 0x6c, 0x58, 0x10, 0x40, + 0xe2, 0x70, 0x12, 0xf2, 0x0b, 0x64, 0xf6, 0xa0, 0x5f, 0x2b, 0xcf, 0x1d, 0x9c, + 0xa7, 0xd0, 0xde, 0x9d, 0x65, 0x29, 0x2b, 0x77, 0x10, ])) .unwrap(), infinity: false, @@ -1273,21 +1232,17 @@ pub mod g1 { fn test_g1_doubling_correctness() { let mut p = G1 { x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, + 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, + 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, + 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, + 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, + 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, + 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, + 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, + 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, ])) .unwrap(), z: Fq::one(), @@ -1301,21 +1256,17 @@ pub mod g1 { p, G1Affine { x: Fq::from_repr(FqRepr([ - 0xf939ddfe0ead7018, - 0x3b03942e732aecb, - 0xce0e9c38fdb11851, - 0x4b914c16687dcde0, - 0x66c8baf177d20533, - 0xaf960cff3d83833 + 0x0a, 0xf9, 0x60, 0xcf, 0xf3, 0xd8, 0x38, 0x33, 0x66, 0xc8, 0xba, 0xf1, 0x77, + 0xd2, 0x05, 0x33, 0x4b, 0x91, 0x4c, 0x16, 0x68, 0x7d, 0xcd, 0xe0, 0xce, 0x0e, + 0x9c, 0x38, 0xfd, 0xb1, 0x18, 0x51, 0x03, 0xb0, 0x39, 0x42, 0xe7, 0x32, 0xae, + 0xcb, 0xf9, 0x39, 0xdd, 0xfe, 0x0e, 0xad, 0x70, 0x18, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x3f0675695f5177a8, - 0x2b6d82ae178a1ba0, - 0x9096380dd8e51b11, - 0x1771a65b60572f4e, - 0x8b547c1313b27555, - 0x135075589a687b1e + 0x13, 0x50, 0x75, 0x58, 0x9a, 0x68, 0x7b, 0x1e, 0x8b, 0x54, 0x7c, 0x13, 0x13, + 0xb2, 0x75, 0x55, 0x17, 0x71, 0xa6, 0x5b, 0x60, 0x57, 0x2f, 0x4e, 0x90, 0x96, + 0x38, 0x0d, 0xd8, 0xe5, 0x1b, 0x11, 0x2b, 0x6d, 0x82, 0xae, 0x17, 0x8a, 0x1b, + 0xa0, 0x3f, 0x06, 0x75, 0x69, 0x5f, 0x51, 0x77, 0xa8, ])) .unwrap(), infinity: false, @@ -1334,21 +1285,17 @@ pub mod g1 { let a = G1Affine { x: Fq::from_repr(FqRepr([ - 0xea431f2cc38fc94d, - 0x3ad2354a07f5472b, - 0xfe669f133f16c26a, - 0x71ffa8021531705, - 0x7418d484386d267, - 0xd5108d8ff1fbd6, + 0x00, 0xd5, 0x10, 0x8d, 0x8f, 0xf1, 0xfb, 0xd6, 0x07, 0x41, 0x8d, 0x48, 0x43, 0x86, + 0xd2, 0x67, 0x07, 0x1f, 0xfa, 0x80, 0x21, 0x53, 0x17, 0x05, 0xfe, 0x66, 0x9f, 0x13, + 0x3f, 0x16, 0xc2, 0x6a, 0x3a, 0xd2, 0x35, 0x4a, 0x07, 0xf5, 0x47, 0x2b, 0xea, 0x43, + 0x1f, 0x2c, 0xc3, 0x8f, 0xc9, 0x4d, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, + 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, + 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, + 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, + 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, ])) .unwrap(), infinity: false, @@ -1356,21 +1303,17 @@ pub mod g1 { let b = G1Affine { x: Fq::from_repr(FqRepr([ - 0xe06cdb156b6356b6, - 0xd9040b2d75448ad9, - 0xe702f14bb0e2aca5, - 0xc6e05201e5f83991, - 0xf7c75910816f207c, - 0x18d4043e78103106, + 0x18, 0xd4, 0x04, 0x3e, 0x78, 0x10, 0x31, 0x06, 0xf7, 0xc7, 0x59, 0x10, 0x81, 0x6f, + 0x20, 0x7c, 0xc6, 0xe0, 0x52, 0x01, 0xe5, 0xf8, 0x39, 0x91, 0xe7, 0x02, 0xf1, 0x4b, + 0xb0, 0xe2, 0xac, 0xa5, 0xd9, 0x04, 0x0b, 0x2d, 0x75, 0x44, 0x8a, 0xd9, 0xe0, 0x6c, + 0xdb, 0x15, 0x6b, 0x63, 0x56, 0xb6, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, + 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, + 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, + 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, + 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, ])) .unwrap(), infinity: false, @@ -1381,21 +1324,17 @@ pub mod g1 { // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 let c = G1Affine { x: Fq::from_repr(FqRepr([ - 0xef4f05bdd10c8aa8, - 0xad5bf87341a2df9, - 0x81c7424206b78714, - 0x9676ff02ec39c227, - 0x4c12c15d7e55b9f3, - 0x57fd1e317db9bd, + 0x00, 0x57, 0xfd, 0x1e, 0x31, 0x7d, 0xb9, 0xbd, 0x4c, 0x12, 0xc1, 0x5d, 0x7e, 0x55, + 0xb9, 0xf3, 0x96, 0x76, 0xff, 0x02, 0xec, 0x39, 0xc2, 0x27, 0x81, 0xc7, 0x42, 0x42, + 0x06, 0xb7, 0x87, 0x14, 0x0a, 0xd5, 0xbf, 0x87, 0x34, 0x1a, 0x2d, 0xf9, 0xef, 0x4f, + 0x05, 0xbd, 0xd1, 0x0c, 0x8a, 0xa8, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x1288334016679345, - 0xf955cd68615ff0b5, - 0xa6998dbaa600f18a, - 0x1267d70db51049fb, - 0x4696deb9ab2ba3e7, - 0xb1e4e11177f59d4, + 0x0b, 0x1e, 0x4e, 0x11, 0x17, 0x7f, 0x59, 0xd4, 0x46, 0x96, 0xde, 0xb9, 0xab, 0x2b, + 0xa3, 0xe7, 0x12, 0x67, 0xd7, 0x0d, 0xb5, 0x10, 0x49, 0xfb, 0xa6, 0x99, 0x8d, 0xba, + 0xa6, 0x00, 0xf1, 0x8a, 0xf9, 0x55, 0xcd, 0x68, 0x61, 0x5f, 0xf0, 0xb5, 0x12, 0x88, + 0x33, 0x40, 0x16, 0x67, 0x93, 0x45, ])) .unwrap(), infinity: false, @@ -1424,10 +1363,10 @@ pub mod g1 { } pub mod g2 { - use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; + use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr}; use super::g1::G1Affine; use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use ff::{BitIterator, Field, PrimeField, SqrtField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; @@ -1517,35 +1456,32 @@ pub mod g2 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x_c0 = FqRepr([0; 6]); - let mut x_c1 = FqRepr([0; 6]); - let mut y_c0 = FqRepr([0; 6]); - let mut y_c1 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); - y_c1.read_be(&mut reader).unwrap(); - y_c0.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x_c1 = FqRepr(copy_segment(©, 0)); + let x_c0 = FqRepr(copy_segment(©, 48)); + let y_c1 = FqRepr(copy_segment(©, 96)); + let y_c0 = FqRepr(copy_segment(©, 144)); + Ok(G2Affine { x: Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + c0: Fq::from_repr(x_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + c1: Fq::from_repr(x_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") })?, }, y: Fq2 { - c0: Fq::from_repr(y_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e) + c0: Fq::from_repr(y_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c0)") })?, - c1: Fq::from_repr(y_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e) + c1: Fq::from_repr(y_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c1)") })?, }, infinity: false, @@ -1560,12 +1496,10 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - let mut writer = &mut res.0[..]; - - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - affine.y.c1.into_repr().write_be(&mut writer).unwrap(); - affine.y.c0.into_repr().write_be(&mut writer).unwrap(); + res.0[0..48].copy_from_slice(&affine.x.c1.into_repr().0); + res.0[48..96].copy_from_slice(&affine.x.c0.into_repr().0); + res.0[96..144].copy_from_slice(&affine.y.c1.into_repr().0); + res.0[144..192].copy_from_slice(&affine.y.c0.into_repr().0); } res @@ -1641,23 +1575,22 @@ pub mod g2 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x_c1 = FqRepr([0; 6]); - let mut x_c0 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x_c1 = FqRepr(copy_segment(©, 0)); + let x_c0 = FqRepr(copy_segment(©, 48)); + // Interpret as Fq element. let x = Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + c0: Fq::from_repr(x_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + c1: Fq::from_repr(x_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") })?, }; @@ -1677,12 +1610,8 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - { - let mut writer = &mut res.0[..]; - - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - } + res.0[..48].copy_from_slice(&affine.x.c1.into_repr().0); + res.0[48..].copy_from_slice(&affine.x.c0.into_repr().0); let negy = affine.y.neg(); @@ -1744,9 +1673,7 @@ pub mod g2 { } impl G2 { - fn empirical_recommended_wnaf_for_scalar(scalar: &FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - + fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { if num_bits >= 103 { 4 } else if num_bits >= 37 { @@ -1827,41 +1754,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xa757072d9fa35ba9, - 0xae3fb2fb418f6e8a, - 0xc1598ec46faa0c7c, - 0x7a17a004747e3dbe, - 0xcc65406a7c2e5a73, - 0x10b8c03d64db4d0c, + 0x10, 0xb8, 0xc0, 0x3d, 0x64, 0xdb, 0x4d, 0x0c, 0xcc, 0x65, 0x40, 0x6a, + 0x7c, 0x2e, 0x5a, 0x73, 0x7a, 0x17, 0xa0, 0x04, 0x74, 0x7e, 0x3d, 0xbe, + 0xc1, 0x59, 0x8e, 0xc4, 0x6f, 0xaa, 0x0c, 0x7c, 0xae, 0x3f, 0xb2, 0xfb, + 0x41, 0x8f, 0x6e, 0x8a, 0xa7, 0x57, 0x07, 0x2d, 0x9f, 0xa3, 0x5b, 0xa9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xd30e70fe2f029778, - 0xda30772df0f5212e, - 0x5b47a9ff9a233a50, - 0xfb777e5b9b568608, - 0x789bac1fec71a2b9, - 0x1342f02e2da54405, + 0x13, 0x42, 0xf0, 0x2e, 0x2d, 0xa5, 0x44, 0x05, 0x78, 0x9b, 0xac, 0x1f, + 0xec, 0x71, 0xa2, 0xb9, 0xfb, 0x77, 0x7e, 0x5b, 0x9b, 0x56, 0x86, 0x08, + 0x5b, 0x47, 0xa9, 0xff, 0x9a, 0x23, 0x3a, 0x50, 0xda, 0x30, 0x77, 0x2d, + 0xf0, 0xf5, 0x21, 0x2e, 0xd3, 0x0e, 0x70, 0xfe, 0x2f, 0x02, 0x97, 0x78, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xfe0812043de54dca, - 0xe455171a3d47a646, - 0xa493f36bc20be98a, - 0x663015d9410eb608, - 0x78e82a79d829a544, - 0x40a00545bb3c1e, + 0x00, 0x40, 0xa0, 0x05, 0x45, 0xbb, 0x3c, 0x1e, 0x78, 0xe8, 0x2a, 0x79, + 0xd8, 0x29, 0xa5, 0x44, 0x66, 0x30, 0x15, 0xd9, 0x41, 0x0e, 0xb6, 0x08, + 0xa4, 0x93, 0xf3, 0x6b, 0xc2, 0x0b, 0xe9, 0x8a, 0xe4, 0x55, 0x17, 0x1a, + 0x3d, 0x47, 0xa6, 0x46, 0xfe, 0x08, 0x12, 0x04, 0x3d, 0xe5, 0x4d, 0xca, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4709802348e79377, - 0xb5ac4dc9204bcfbd, - 0xda361c97d02f42b2, - 0x15008b1dc399e8df, - 0x68128fd0548a3829, - 0x16a613db5c873aaa, + 0x16, 0xa6, 0x13, 0xdb, 0x5c, 0x87, 0x3a, 0xaa, 0x68, 0x12, 0x8f, 0xd0, + 0x54, 0x8a, 0x38, 0x29, 0x15, 0x00, 0x8b, 0x1d, 0xc3, 0x99, 0xe8, 0xdf, + 0xda, 0x36, 0x1c, 0x97, 0xd0, 0x2f, 0x42, 0xb2, 0xb5, 0xac, 0x4d, 0xc9, + 0x20, 0x4b, 0xcf, 0xbd, 0x47, 0x09, 0x80, 0x23, 0x48, 0xe7, 0x93, 0x77, ])) .unwrap(), }, @@ -1876,41 +1795,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf4fdfe95a705f917, - 0xc2914df688233238, - 0x37c6b12cca35a34b, - 0x41abba710d6c692c, - 0xffcc4b2b62ce8484, - 0x6993ec01b8934ed, + 0x06, 0x99, 0x3e, 0xc0, 0x1b, 0x89, 0x34, 0xed, 0xff, 0xcc, 0x4b, 0x2b, + 0x62, 0xce, 0x84, 0x84, 0x41, 0xab, 0xba, 0x71, 0x0d, 0x6c, 0x69, 0x2c, + 0x37, 0xc6, 0xb1, 0x2c, 0xca, 0x35, 0xa3, 0x4b, 0xc2, 0x91, 0x4d, 0xf6, + 0x88, 0x23, 0x32, 0x38, 0xf4, 0xfd, 0xfe, 0x95, 0xa7, 0x05, 0xf9, 0x17, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xb94e92d5f874e26, - 0x44516408bc115d95, - 0xe93946b290caa591, - 0xa5a0c2b7131f3555, - 0x83800965822367e7, - 0x10cf1d3ad8d90bfa, + 0x10, 0xcf, 0x1d, 0x3a, 0xd8, 0xd9, 0x0b, 0xfa, 0x83, 0x80, 0x09, 0x65, + 0x82, 0x23, 0x67, 0xe7, 0xa5, 0xa0, 0xc2, 0xb7, 0x13, 0x1f, 0x35, 0x55, + 0xe9, 0x39, 0x46, 0xb2, 0x90, 0xca, 0xa5, 0x91, 0x44, 0x51, 0x64, 0x08, + 0xbc, 0x11, 0x5d, 0x95, 0x0b, 0x94, 0xe9, 0x2d, 0x5f, 0x87, 0x4e, 0x26, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xbf00334c79701d97, - 0x4fe714f9ff204f9a, - 0xab70b28002f3d825, - 0x5a9171720e73eb51, - 0x38eb4fd8d658adb7, - 0xb649051bbc1164d, + 0x0b, 0x64, 0x90, 0x51, 0xbb, 0xc1, 0x16, 0x4d, 0x38, 0xeb, 0x4f, 0xd8, + 0xd6, 0x58, 0xad, 0xb7, 0x5a, 0x91, 0x71, 0x72, 0x0e, 0x73, 0xeb, 0x51, + 0xab, 0x70, 0xb2, 0x80, 0x02, 0xf3, 0xd8, 0x25, 0x4f, 0xe7, 0x14, 0xf9, + 0xff, 0x20, 0x4f, 0x9a, 0xbf, 0x00, 0x33, 0x4c, 0x79, 0x70, 0x1d, 0x97, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x9225814253d7df75, - 0xc196c2513477f887, - 0xe05e2fbd15a804e0, - 0x55f2b8efad953e04, - 0x7379345eda55265e, - 0x377f2e6208fd4cb, + 0x03, 0x77, 0xf2, 0xe6, 0x20, 0x8f, 0xd4, 0xcb, 0x73, 0x79, 0x34, 0x5e, + 0xda, 0x55, 0x26, 0x5e, 0x55, 0xf2, 0xb8, 0xef, 0xad, 0x95, 0x3e, 0x04, + 0xe0, 0x5e, 0x2f, 0xbd, 0x15, 0xa8, 0x04, 0xe0, 0xc1, 0x96, 0xc2, 0x51, + 0x34, 0x77, 0xf8, 0x87, 0x92, 0x25, 0x81, 0x42, 0x53, 0xd7, 0xdf, 0x75, ])) .unwrap(), }, @@ -1926,41 +1837,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x262cea73ea1906c, - 0x2f08540770fabd6, - 0x4ceb92d0a76057be, - 0x2199bc19c48c393d, - 0x4a151b732a6075bf, - 0x17762a3b9108c4a7, + 0x17, 0x76, 0x2a, 0x3b, 0x91, 0x08, 0xc4, 0xa7, 0x4a, 0x15, 0x1b, 0x73, + 0x2a, 0x60, 0x75, 0xbf, 0x21, 0x99, 0xbc, 0x19, 0xc4, 0x8c, 0x39, 0x3d, + 0x4c, 0xeb, 0x92, 0xd0, 0xa7, 0x60, 0x57, 0xbe, 0x02, 0xf0, 0x85, 0x40, + 0x77, 0x0f, 0xab, 0xd6, 0x02, 0x62, 0xce, 0xa7, 0x3e, 0xa1, 0x90, 0x6c, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x26f461e944bbd3d1, - 0x298f3189a9cf6ed6, - 0x74328ad8bc2aa150, - 0x7e147f3f9e6e241, - 0x72a9b63583963fff, - 0x158b0083c000462, + 0x01, 0x58, 0xb0, 0x08, 0x3c, 0x00, 0x04, 0x62, 0x72, 0xa9, 0xb6, 0x35, + 0x83, 0x96, 0x3f, 0xff, 0x07, 0xe1, 0x47, 0xf3, 0xf9, 0xe6, 0xe2, 0x41, + 0x74, 0x32, 0x8a, 0xd8, 0xbc, 0x2a, 0xa1, 0x50, 0x29, 0x8f, 0x31, 0x89, + 0xa9, 0xcf, 0x6e, 0xd6, 0x26, 0xf4, 0x61, 0xe9, 0x44, 0xbb, 0xd3, 0xd1, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x91fb0b225ecf103b, - 0x55d42edc1dc46ba0, - 0x43939b11997b1943, - 0x68cad19430706b4d, - 0x3ccfb97b924dcea8, - 0x1660f93434588f8d, + 0x16, 0x60, 0xf9, 0x34, 0x34, 0x58, 0x8f, 0x8d, 0x3c, 0xcf, 0xb9, 0x7b, + 0x92, 0x4d, 0xce, 0xa8, 0x68, 0xca, 0xd1, 0x94, 0x30, 0x70, 0x6b, 0x4d, + 0x43, 0x93, 0x9b, 0x11, 0x99, 0x7b, 0x19, 0x43, 0x55, 0xd4, 0x2e, 0xdc, + 0x1d, 0xc4, 0x6b, 0xa0, 0x91, 0xfb, 0x0b, 0x22, 0x5e, 0xcf, 0x10, 0x3b, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xaaed3985b6dcb9c7, - 0xc1e985d6d898d9f4, - 0x618bd2ac3271ac42, - 0x3940a2dbb914b529, - 0xbeb88137cf34f3e7, - 0x1699ee577c61b694, + 0x16, 0x99, 0xee, 0x57, 0x7c, 0x61, 0xb6, 0x94, 0xbe, 0xb8, 0x81, 0x37, + 0xcf, 0x34, 0xf3, 0xe7, 0x39, 0x40, 0xa2, 0xdb, 0xb9, 0x14, 0xb5, 0x29, + 0x61, 0x8b, 0xd2, 0xac, 0x32, 0x71, 0xac, 0x42, 0xc1, 0xe9, 0x85, 0xd6, + 0xd8, 0x98, 0xd9, 0xf4, 0xaa, 0xed, 0x39, 0x85, 0xb6, 0xdc, 0xb9, 0xc7, ])) .unwrap(), }, @@ -1976,41 +1879,33 @@ pub mod g2 { let mut p = G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, + 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, + 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, + 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, + 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, + 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, + 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, + 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, + 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, + 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, + 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, + 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, + 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, + 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, + 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, + 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, + 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, ])) .unwrap(), }, @@ -2020,41 +1915,33 @@ pub mod g2 { p.add_assign(&G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xa8c763d25910bdd3, - 0x408777b30ca3add4, - 0x6115fcc12e2769e, - 0x8e73a96b329ad190, - 0x27c546f75ee1f3ab, - 0xa33d27add5e7e82, + 0x0a, 0x33, 0xd2, 0x7a, 0xdd, 0x5e, 0x7e, 0x82, 0x27, 0xc5, 0x46, 0xf7, 0x5e, + 0xe1, 0xf3, 0xab, 0x8e, 0x73, 0xa9, 0x6b, 0x32, 0x9a, 0xd1, 0x90, 0x06, 0x11, + 0x5f, 0xcc, 0x12, 0xe2, 0x76, 0x9e, 0x40, 0x87, 0x77, 0xb3, 0x0c, 0xa3, 0xad, + 0xd4, 0xa8, 0xc7, 0x63, 0xd2, 0x59, 0x10, 0xbd, 0xd3, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x93b1ebcd54870dfe, - 0xf1578300e1342e11, - 0x8270dca3a912407b, - 0x2089faf462438296, - 0x828e5848cd48ea66, - 0x141ecbac1deb038b, + 0x14, 0x1e, 0xcb, 0xac, 0x1d, 0xeb, 0x03, 0x8b, 0x82, 0x8e, 0x58, 0x48, 0xcd, + 0x48, 0xea, 0x66, 0x20, 0x89, 0xfa, 0xf4, 0x62, 0x43, 0x82, 0x96, 0x82, 0x70, + 0xdc, 0xa3, 0xa9, 0x12, 0x40, 0x7b, 0xf1, 0x57, 0x83, 0x00, 0xe1, 0x34, 0x2e, + 0x11, 0x93, 0xb1, 0xeb, 0xcd, 0x54, 0x87, 0x0d, 0xfe, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf5d2c28857229c3f, - 0x8c1574228757ca23, - 0xe8d8102175f5dc19, - 0x2767032fc37cc31d, - 0xd5ee2aba84fd10fe, - 0x16576ccd3dd0a4e8, + 0x16, 0x57, 0x6c, 0xcd, 0x3d, 0xd0, 0xa4, 0xe8, 0xd5, 0xee, 0x2a, 0xba, 0x84, + 0xfd, 0x10, 0xfe, 0x27, 0x67, 0x03, 0x2f, 0xc3, 0x7c, 0xc3, 0x1d, 0xe8, 0xd8, + 0x10, 0x21, 0x75, 0xf5, 0xdc, 0x19, 0x8c, 0x15, 0x74, 0x22, 0x87, 0x57, 0xca, + 0x23, 0xf5, 0xd2, 0xc2, 0x88, 0x57, 0x22, 0x9c, 0x3f, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4da9b6f6a96d1dd2, - 0x9657f7da77f1650e, - 0xbc150712f9ffe6da, - 0x31898db63f87363a, - 0xabab040ddbd097cc, - 0x11ad236b9ba02990, + 0x11, 0xad, 0x23, 0x6b, 0x9b, 0xa0, 0x29, 0x90, 0xab, 0xab, 0x04, 0x0d, 0xdb, + 0xd0, 0x97, 0xcc, 0x31, 0x89, 0x8d, 0xb6, 0x3f, 0x87, 0x36, 0x3a, 0xbc, 0x15, + 0x07, 0x12, 0xf9, 0xff, 0xe6, 0xda, 0x96, 0x57, 0xf7, 0xda, 0x77, 0xf1, 0x65, + 0x0e, 0x4d, 0xa9, 0xb6, 0xf6, 0xa9, 0x6d, 0x1d, 0xd2, ])) .unwrap(), }, @@ -2068,41 +1955,33 @@ pub mod g2 { G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xcde7ee8a3f2ac8af, - 0xfc642eb35975b069, - 0xa7de72b7dd0e64b7, - 0xf1273e6406eef9cc, - 0xababd760ff05cb92, - 0xd7c20456617e89 + 0x00, 0xd7, 0xc2, 0x04, 0x56, 0x61, 0x7e, 0x89, 0xab, 0xab, 0xd7, 0x60, + 0xff, 0x05, 0xcb, 0x92, 0xf1, 0x27, 0x3e, 0x64, 0x06, 0xee, 0xf9, 0xcc, + 0xa7, 0xde, 0x72, 0xb7, 0xdd, 0x0e, 0x64, 0xb7, 0xfc, 0x64, 0x2e, 0xb3, + 0x59, 0x75, 0xb0, 0x69, 0xcd, 0xe7, 0xee, 0x8a, 0x3f, 0x2a, 0xc8, 0xaf, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xd1a50b8572cbd2b8, - 0x238f0ac6119d07df, - 0x4dbe924fe5fd6ac2, - 0x8b203284c51edf6b, - 0xc8a0b730bbb21f5e, - 0x1a3b59d29a31274 + 0x01, 0xa3, 0xb5, 0x9d, 0x29, 0xa3, 0x12, 0x74, 0xc8, 0xa0, 0xb7, 0x30, + 0xbb, 0xb2, 0x1f, 0x5e, 0x8b, 0x20, 0x32, 0x84, 0xc5, 0x1e, 0xdf, 0x6b, + 0x4d, 0xbe, 0x92, 0x4f, 0xe5, 0xfd, 0x6a, 0xc2, 0x23, 0x8f, 0x0a, 0xc6, + 0x11, 0x9d, 0x07, 0xdf, 0xd1, 0xa5, 0x0b, 0x85, 0x72, 0xcb, 0xd2, 0xb8, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x9e709e78a8eaa4c9, - 0xd30921c93ec342f4, - 0x6d1ef332486f5e34, - 0x64528ab3863633dc, - 0x159384333d7cba97, - 0x4cb84741f3cafe8 + 0x04, 0xcb, 0x84, 0x74, 0x1f, 0x3c, 0xaf, 0xe8, 0x15, 0x93, 0x84, 0x33, + 0x3d, 0x7c, 0xba, 0x97, 0x64, 0x52, 0x8a, 0xb3, 0x86, 0x36, 0x33, 0xdc, + 0x6d, 0x1e, 0xf3, 0x32, 0x48, 0x6f, 0x5e, 0x34, 0xd3, 0x09, 0x21, 0xc9, + 0x3e, 0xc3, 0x42, 0xf4, 0x9e, 0x70, 0x9e, 0x78, 0xa8, 0xea, 0xa4, 0xc9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x242af0dc3640e1a4, - 0xe90a73ad65c66919, - 0x2bd7ca7f4346f9ec, - 0x38528f92b689644d, - 0xb6884deec59fb21f, - 0x3c075d3ec52ba90 + 0x03, 0xc0, 0x75, 0xd3, 0xec, 0x52, 0xba, 0x90, 0xb6, 0x88, 0x4d, 0xee, + 0xc5, 0x9f, 0xb2, 0x1f, 0x38, 0x52, 0x8f, 0x92, 0xb6, 0x89, 0x64, 0x4d, + 0x2b, 0xd7, 0xca, 0x7f, 0x43, 0x46, 0xf9, 0xec, 0xe9, 0x0a, 0x73, 0xad, + 0x65, 0xc6, 0x69, 0x19, 0x24, 0x2a, 0xf0, 0xdc, 0x36, 0x40, 0xe1, 0xa4, ])) .unwrap(), }, @@ -2116,41 +1995,33 @@ pub mod g2 { let mut p = G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, + 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, + 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, + 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, + 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, + 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, + 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, + 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, + 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, + 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, + 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, + 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, + 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, + 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, + 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, + 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, + 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, ])) .unwrap(), }, @@ -2166,41 +2037,33 @@ pub mod g2 { G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x91ccb1292727c404, - 0x91a6cb182438fad7, - 0x116aee59434de902, - 0xbcedcfce1e52d986, - 0x9755d4a3926e9862, - 0x18bab73760fd8024 + 0x18, 0xba, 0xb7, 0x37, 0x60, 0xfd, 0x80, 0x24, 0x97, 0x55, 0xd4, 0xa3, + 0x92, 0x6e, 0x98, 0x62, 0xbc, 0xed, 0xcf, 0xce, 0x1e, 0x52, 0xd9, 0x86, + 0x11, 0x6a, 0xee, 0x59, 0x43, 0x4d, 0xe9, 0x02, 0x91, 0xa6, 0xcb, 0x18, + 0x24, 0x38, 0xfa, 0xd7, 0x91, 0xcc, 0xb1, 0x29, 0x27, 0x27, 0xc4, 0x04, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4e7c5e0a2ae5b99e, - 0x96e582a27f028961, - 0xc74d1cf4ef2d5926, - 0xeb0cf5e610ef4fe7, - 0x7b4c2bae8db6e70b, - 0xf136e43909fca0 + 0x00, 0xf1, 0x36, 0xe4, 0x39, 0x09, 0xfc, 0xa0, 0x7b, 0x4c, 0x2b, 0xae, + 0x8d, 0xb6, 0xe7, 0x0b, 0xeb, 0x0c, 0xf5, 0xe6, 0x10, 0xef, 0x4f, 0xe7, + 0xc7, 0x4d, 0x1c, 0xf4, 0xef, 0x2d, 0x59, 0x26, 0x96, 0xe5, 0x82, 0xa2, + 0x7f, 0x02, 0x89, 0x61, 0x4e, 0x7c, 0x5e, 0x0a, 0x2a, 0xe5, 0xb9, 0x9e, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x954d4466ab13e58, - 0x3ee42eec614cf890, - 0x853bb1d28877577e, - 0xa5a2a51f7fde787b, - 0x8b92866bc6384188, - 0x81a53fe531d64ef + 0x08, 0x1a, 0x53, 0xfe, 0x53, 0x1d, 0x64, 0xef, 0x8b, 0x92, 0x86, 0x6b, + 0xc6, 0x38, 0x41, 0x88, 0xa5, 0xa2, 0xa5, 0x1f, 0x7f, 0xde, 0x78, 0x7b, + 0x85, 0x3b, 0xb1, 0xd2, 0x88, 0x77, 0x57, 0x7e, 0x3e, 0xe4, 0x2e, 0xec, + 0x61, 0x4c, 0xf8, 0x90, 0x09, 0x54, 0xd4, 0x46, 0x6a, 0xb1, 0x3e, 0x58, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4c5d607666239b34, - 0xeddb5f48304d14b3, - 0x337167ee6e8e3cb6, - 0xb271f52f12ead742, - 0x244e6c2015c83348, - 0x19e2deae6eb9b441 + 0x19, 0xe2, 0xde, 0xae, 0x6e, 0xb9, 0xb4, 0x41, 0x24, 0x4e, 0x6c, 0x20, + 0x15, 0xc8, 0x33, 0x48, 0xb2, 0x71, 0xf5, 0x2f, 0x12, 0xea, 0xd7, 0x42, + 0x33, 0x71, 0x67, 0xee, 0x6e, 0x8e, 0x3c, 0xb6, 0xed, 0xdb, 0x5f, 0x48, + 0x30, 0x4d, 0x14, 0xb3, 0x4c, 0x5d, 0x60, 0x76, 0x66, 0x23, 0x9b, 0x34, ])) .unwrap(), }, diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index f9caf5e95e..fa236ff9c5 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1,5 +1,5 @@ use super::fq2::Fq2; -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use std::ops::{AddAssign, MulAssign, SubAssign}; #[cfg(test)] @@ -8,14 +8,14 @@ use ff::PowVartime; use std::ops::Neg; // B coefficient of BLS12-381 curve, 4. -pub const B_COEFF: Fq = Fq(FqRepr([ +pub const B_COEFF: Fq = Fq([ 0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x9d645513d83de7e, -])); +]); // The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, // and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the @@ -24,228 +24,228 @@ pub const B_COEFF: Fq = Fq(FqRepr([ // Generator of G1 // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ +pub const G1_GENERATOR_X: Fq = Fq([ 0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75, -])); -pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ +]); +pub const G1_GENERATOR_Y: Fq = Fq([ 0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, 0x51ac582950405194, 0xe1c8c3fad0059c0, 0xbbc3efc5008a26a, -])); +]); // Generator of G2 // x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 // y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 -pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ +pub const G2_GENERATOR_X_C0: Fq = Fq([ 0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, 0x6f67b7631863366b, 0x58191924350bcd7, -])); -pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_X_C1: Fq = Fq([ 0xa5a9c0759e23f606, 0xaaa0c59dbccd60c3, 0x3bb17e18e2867806, 0x1b1ab6cc8541b367, 0xc2b6ed0ef2158547, 0x11922a097360edf3, -])); -pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_Y_C0: Fq = Fq([ 0x4c730af860494c4a, 0x597cfa1f5e369c5a, 0xe7e6856caa0a635a, 0xbbefb5e96e0d495f, 0x7d3a975f0ef25a2, 0x83fd8e7e80dae5, -])); -pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_Y_C1: Fq = Fq([ 0xadc0fc92df64b05d, 0x18aa270a2b1461dc, 0x86adac6a3be4eba0, 0x79495c4ec93da33a, 0xe7175850a43ccaed, 0xb2bc2a163de1bf2, -])); +]); // Coefficients for the Frobenius automorphism. pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ // Fq(-1)**(((q^0) - 1) / 2) - Fq(FqRepr([ + Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), + ]), // Fq(-1)**(((q^1) - 1) / 2) - Fq(FqRepr([ + Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), + ]), ]; pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ // Fq2(u + 1)**(((q^0) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^1) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), + ]), }, // Fq2(u + 1)**(((q^2) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^3) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), + ]), }, // Fq2(u + 1)**(((q^4) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^5) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), + ]), }, ]; pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ // Fq2(u + 1)**(((2q^0) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^1) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^2) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^3) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^4) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^5) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, ]; @@ -253,206 +253,207 @@ pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ // Fq2(u + 1)**(((q^0) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^1) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf, - ])), + ]), }, // Fq2(u + 1)**(((q^2) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^3) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2, - ])), + ]), }, // Fq2(u + 1)**(((q^4) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^5) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd, - ])), + ]), }, // Fq2(u + 1)**(((q^6) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^7) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb, - ])), + ]), }, // Fq2(u + 1)**(((q^8) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^9) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8, - ])), + ]), }, // Fq2(u + 1)**(((q^10) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^11) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd, - ])), + ]), }, ]; // -((2**384) mod q) mod q -pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ +pub const NEGATIVE_ONE: Fq = Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, -])); +]); #[derive(PrimeField)] #[PrimeFieldModulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787"] #[PrimeFieldGenerator = "2"] -pub struct Fq(FqRepr); +#[PrimeFieldReprEndianness = "big"] +pub struct Fq([u64; 6]); #[test] fn test_b_coeff() { @@ -1182,428 +1183,30 @@ use rand_core::SeedableRng; #[cfg(test)] use rand_xorshift::XorShiftRng; -#[test] -fn test_fq_repr_ordering() { - use std::cmp::Ordering; - - fn assert_equality(a: FqRepr, b: FqRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == Ordering::Equal); - } - - fn assert_lt(a: FqRepr, b: FqRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9999, 9998]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); -} - -#[test] -fn test_fq_repr_from() { - assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_fq_repr_is_odd() { - assert!(!FqRepr::from(0).is_odd()); - assert!(FqRepr::from(0).is_even()); - assert!(FqRepr::from(1).is_odd()); - assert!(!FqRepr::from(1).is_even()); - assert!(!FqRepr::from(324834872).is_odd()); - assert!(FqRepr::from(324834872).is_even()); - assert!(FqRepr::from(324834873).is_odd()); - assert!(!FqRepr::from(324834873).is_even()); -} - -#[test] -fn test_fq_repr_is_zero() { - assert!(FqRepr::from(0).is_zero()); - assert!(!FqRepr::from(1).is_zero()); - assert!(!FqRepr([0, 0, 0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fq_repr_div2() { - let mut a = FqRepr([ - 0x8b0ad39f8dd7482a, - 0x147221c9a7178b69, - 0x54764cb08d8a6aa0, - 0x8519d708e1d83041, - 0x41f82777bd13fdb, - 0xf43944578f9b771b, - ]); - a.div2(); - assert_eq!( - a, - FqRepr([ - 0xc58569cfc6eba415, - 0xa3910e4d38bc5b4, - 0xaa3b265846c53550, - 0xc28ceb8470ec1820, - 0x820fc13bbde89fed, - 0x7a1ca22bc7cdbb8d - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FqRepr([ - 0x6d31615a73f1bae9, - 0x54028e443934e2f1, - 0x82a8ec99611b14d, - 0xfb70a33ae11c3b06, - 0xe36083f04eef7a27, - 0x1e87288af1f36e - ]) - ); - for _ in 0..300 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7288af1f36ee3608, 0x1e8, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..50 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7a1ca2, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..22 { - a.div2(); - } - assert_eq!(a, FqRepr([0x1, 0x0, 0x0, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_shr() { - let mut a = FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e, - ]); - a.shr(0); - assert_eq!( - a, - FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e - ]) - ); - a.shr(1); - assert_eq!( - a, - FqRepr([ - 0xd52e6eb0b9423ffe, - 0x21921603576aa943, - 0xceeead98979ee882, - 0xe2aa0fea40235bf3, - 0xb04067a038f0582, - 0x912f9480d7528a7 - ]) - ); - a.shr(50); - assert_eq!( - a, - FqRepr([ - 0x8580d5daaa50f54b, - 0xab6625e7ba208864, - 0x83fa9008d6fcf3bb, - 0x19e80e3c160b8aa, - 0xbe52035d4a29c2c1, - 0x244 - ]) - ); - a.shr(130); - assert_eq!( - a, - FqRepr([ - 0xa0fea40235bf3cee, - 0x4067a038f0582e2a, - 0x2f9480d7528a70b0, - 0x91, - 0x0, - 0x0 - ]) - ); - a.shr(64); - assert_eq!( - a, - FqRepr([0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0, 0x0]) - ); -} - -#[test] -fn test_fq_repr_mul2() { - let mut a = FqRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FqRepr([0xb0acd6c96, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!( - a, - FqRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0, 0x0, 0x0]) - ); - for _ in 0..300 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0xcd6c960000000000])); - for _ in 0..17 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x2c00000000000000])); - for _ in 0..6 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_num_bits() { - let mut a = FqRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FqRepr::from(1); - for i in 1..385 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fq_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.sub_noborrow(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0x40a12b8967c54bae, - 0xdeae37a0837d0d7b, - 0xe592c487bae374e, - 0xaf26bbc934462a61, - 0x32d6cc6e2b7a4a03, - 0xcdaf23e091c0313 - ]) - ); - - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - a.0[5] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } - - // Subtracting q+1 from q should produce -1 (mod 2**384) - let mut qplusone = FqRepr([ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ]); - qplusone.sub_noborrow(&FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ])); - assert_eq!( - qplusone, - FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ); -} - -#[test] -fn test_fq_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.add_nocarry(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0xcfae1db798be8c04, - 0x999906db15a10d5a, - 0x270fa8d9defc6f79, - 0x83abb199c240f7b6, - 0x27469abae93e1ff6, - 0xdd2fd2d4dfab6be - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - let mut b = Fq::random(&mut rng).into_repr(); - let mut c = Fq::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[5] >>= 3; - b.0[5] >>= 3; - c.0[5] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } - - // Adding 1 to (2^384 - 1) should produce zero - let mut x = FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - x.add_nocarry(&FqRepr::from(1)); - assert!(x.is_zero()); -} - #[test] fn test_fq_is_valid() { - let mut a = Fq(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FqRepr::from(1)); + a.sub_noborrow(&Fq([1, 0, 0, 0, 0, 0])); assert!(a.is_valid()); assert!(Fq::from(0).is_valid()); - assert!(Fq(FqRepr([ + assert!(Fq([ 0xdf4671abd14dab3e, 0xe2dc0c9f534fbd33, 0x31ca6c880cc444a6, 0x257a67e70ef33359, 0xf9b29e493f899b36, 0x17c8be1800b9f059 - ])) + ]) .is_valid()); - assert!(!Fq(FqRepr([ + assert!(!Fq([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -1621,103 +1224,103 @@ fn test_fq_is_valid() { fn test_fq_add_assign() { { // Random number - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0x624434821df92b69, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b, - ])); + ]); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fq(FqRepr::from(0))); + tmp.add_assign(&Fq([0, 0, 0, 0, 0, 0])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x624434821df92b69, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fq(FqRepr::from(1))); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x624434821df92b6a, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fq(FqRepr([ + tmp.add_assign(&Fq([ 0x374d8f8ea7a648d8, 0xe318bb0ebb8bfa9b, 0x613d996f0a95b400, 0x9fac233cb7e4fef1, 0x67e47552d253c52, 0x5c31b227edf25da, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0xdf92c410c59fc997, 0x149f1bd05a0add85, 0xd3ec393c20fba6ab, 0x37001165c1bde71d, 0x421b41c9f662408e, 0x21c38104f435f5b - ])) + ]) ); // Add one to (q - 1) and test for the result. - tmp = Fq(FqRepr([ + tmp = Fq([ 0xb9feffffffffaaaa, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a, - ])); - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is q - 1 - tmp = Fq(FqRepr([ + tmp = Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ])); - tmp.add_assign(&Fq(FqRepr([ + ]); + tmp.add_assign(&Fq([ 0x66ecde5bef0fe14f, 0xac2a6cf8aed568e8, 0x861d70d86483edd, 0xcc98f1b7839a22e8, 0x6ee5e2a4eae7674e, 0x194e40737930c599, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0xb9feffffffffaaaa, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -1751,87 +1354,87 @@ fn test_fq_add_assign() { fn test_fq_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ])); - tmp.sub_assign(&Fq(FqRepr([ + ]); + tmp.sub_assign(&Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x748014838971292c, 0xfd20fad49fddde5c, 0xcf87f198e3d3f336, 0x3d62d6e6e41883db, 0x45a3443cd88dc61b, 0x151d57aaf755ff94 - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fq(FqRepr([ + tmp = Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr([ + ]); + tmp.sub_assign(&Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x457eeb7c768e817f, 0x218b052a117621a3, 0x97a8e10812dd02ed, 0x2714749e0f6c8ee3, 0x57863796abde6bc, 0x4e3ba3f4229e706 - ])) + ]) ); // Test for sensible results with zero - tmp = Fq(FqRepr::from(0)); - tmp.sub_assign(&Fq(FqRepr::from(0))); + tmp = Fq::zero(); + tmp.sub_assign(&Fq::zero()); assert!(tmp.is_zero()); - tmp = Fq(FqRepr([ + tmp = Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr::from(0))); + ]); + tmp.sub_assign(&Fq::zero()); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806 - ])) + ]) ); } @@ -1858,31 +1461,31 @@ fn test_fq_sub_assign() { #[test] fn test_fq_mul_assign() { - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0xcc6200000020aa8a, 0x422800801dd8001a, 0x7f4f5e619041c62c, 0x8a55171ac70ed2ba, 0x3f69cc3a3d07d58b, 0xb972455fd09b8ef, - ])); - tmp.mul_assign(&Fq(FqRepr([ + ]); + tmp.mul_assign(&Fq([ 0x329300000030ffcf, 0x633c00c02cc40028, 0xbef70d925862a942, 0x4f7fa2a82a963c17, 0xdf1eb2575b8bc051, 0x1162b680fb8e9566, - ]))); + ])); assert!( - tmp == Fq(FqRepr([ + tmp == Fq([ 0x9dc4000001ebfe14, 0x2850078997b00193, 0xa8197f1abb4d7bf, 0xc0309573f4bfe871, 0xf48d0923ffaf7620, 0x11d4b58c7a926e66 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -1934,96 +1537,82 @@ fn test_fq_mul_assign() { #[test] fn test_fq_shr() { let mut a = Fq::from_repr(FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e, + 0x12, 0x25, 0xf2, 0x90, 0x1a, 0xea, 0x51, 0x4e, 0x16, 0x08, 0x0c, 0xf4, 0x07, 0x1e, 0x0b, + 0x05, 0xc5, 0x54, 0x1f, 0xd4, 0x80, 0x46, 0xb7, 0xe7, 0x9d, 0xdd, 0x5b, 0x31, 0x2f, 0x3d, + 0xd1, 0x04, 0x43, 0x24, 0x2c, 0x06, 0xae, 0xd5, 0x52, 0x87, 0xaa, 0x5c, 0xdd, 0x61, 0x72, + 0x84, 0x7f, 0xfd, ])) .unwrap(); a = a >> 0; assert_eq!( a.into_repr(), FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e, + 0x12, 0x25, 0xf2, 0x90, 0x1a, 0xea, 0x51, 0x4e, 0x16, 0x08, 0x0c, 0xf4, 0x07, 0x1e, + 0x0b, 0x05, 0xc5, 0x54, 0x1f, 0xd4, 0x80, 0x46, 0xb7, 0xe7, 0x9d, 0xdd, 0x5b, 0x31, + 0x2f, 0x3d, 0xd1, 0x04, 0x43, 0x24, 0x2c, 0x06, 0xae, 0xd5, 0x52, 0x87, 0xaa, 0x5c, + 0xdd, 0x61, 0x72, 0x84, 0x7f, 0xfd, ]) ); a = a >> 1; assert_eq!( a.into_repr(), FqRepr([ - 0xd52e6eb0b9423ffe, - 0x21921603576aa943, - 0xceeead98979ee882, - 0xe2aa0fea40235bf3, - 0x0b04067a038f0582, - 0x0912f9480d7528a7, + 0x09, 0x12, 0xf9, 0x48, 0x0d, 0x75, 0x28, 0xa7, 0x0b, 0x04, 0x06, 0x7a, 0x03, 0x8f, + 0x05, 0x82, 0xe2, 0xaa, 0x0f, 0xea, 0x40, 0x23, 0x5b, 0xf3, 0xce, 0xee, 0xad, 0x98, + 0x97, 0x9e, 0xe8, 0x82, 0x21, 0x92, 0x16, 0x03, 0x57, 0x6a, 0xa9, 0x43, 0xd5, 0x2e, + 0x6e, 0xb0, 0xb9, 0x42, 0x3f, 0xfe, ]) ); a = a >> 50; assert_eq!( a.into_repr(), FqRepr([ - 0x8580d5daaa50f54b, - 0xab6625e7ba208864, - 0x83fa9008d6fcf3bb, - 0x019e80e3c160b8aa, - 0xbe52035d4a29c2c1, - 0x0000000000000244, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0xbe, 0x52, 0x03, 0x5d, 0x4a, 0x29, + 0xc2, 0xc1, 0x01, 0x9e, 0x80, 0xe3, 0xc1, 0x60, 0xb8, 0xaa, 0x83, 0xfa, 0x90, 0x08, + 0xd6, 0xfc, 0xf3, 0xbb, 0xab, 0x66, 0x25, 0xe7, 0xba, 0x20, 0x88, 0x64, 0x85, 0x80, + 0xd5, 0xda, 0xaa, 0x50, 0xf5, 0x4b, ]) ); a = a >> 130; assert_eq!( a.into_repr(), FqRepr([ - 0xa0fea40235bf3cee, - 0x4067a038f0582e2a, - 0x2f9480d7528a70b0, - 0x0000000000000091, - 0x0000000000000000, - 0x0000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x2f, 0x94, 0x80, 0xd7, + 0x52, 0x8a, 0x70, 0xb0, 0x40, 0x67, 0xa0, 0x38, 0xf0, 0x58, 0x2e, 0x2a, 0xa0, 0xfe, + 0xa4, 0x02, 0x35, 0xbf, 0x3c, 0xee, ]) ); a = a >> 64; assert_eq!( a.into_repr(), FqRepr([ - 0x4067a038f0582e2a, - 0x2f9480d7528a70b0, - 0x0000000000000091, - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x91, 0x2f, 0x94, 0x80, 0xd7, 0x52, 0x8a, 0x70, 0xb0, 0x40, 0x67, + 0xa0, 0x38, 0xf0, 0x58, 0x2e, 0x2a, ]) ); } #[test] fn test_fq_squaring() { - let a = Fq(FqRepr([ + let a = Fq([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x19ffffffffffffff, - ])); + ]); assert!(a.is_valid()); assert_eq!( a.square(), Fq::from_repr(FqRepr([ - 0x1cfb28fe7dfbbb86, - 0x24cbe1731577a59, - 0xcce1d4edc120e66e, - 0xdc05c659b4e15b27, - 0x79361e5a802c6a23, - 0x24bcbe5d51b9a6f + 0x02, 0x4b, 0xcb, 0xe5, 0xd5, 0x1b, 0x9a, 0x6f, 0x79, 0x36, 0x1e, 0x5a, 0x80, 0x2c, + 0x6a, 0x23, 0xdc, 0x05, 0xc6, 0x59, 0xb4, 0xe1, 0x5b, 0x27, 0xcc, 0xe1, 0xd4, 0xed, + 0xc1, 0x20, 0xe6, 0x6e, 0x02, 0x4c, 0xbe, 0x17, 0x31, 0x57, 0x7a, 0x59, 0x1c, 0xfb, + 0x28, 0xfe, 0x7d, 0xfb, 0xbb, 0x86, ])) .unwrap() ); @@ -2161,50 +1750,42 @@ fn test_fq_sqrt() { fn test_fq_from_into_repr() { // q + 1 should not be in the field assert!(Fq::from_repr(FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, + 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, + 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xaa, 0xac, ])) - .is_err()); + .is_none()); // q should not be in the field - assert!(Fq::from_repr(Fq::char()).is_err()); + assert!(Fq::from_repr(Fq::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. let a = FqRepr([ - 0x4a49dad4ff6cde2d, - 0xac62a82a8f51cd50, - 0x2b1f41ab9f36d640, - 0x908a387f480735f1, - 0xae30740c08a875d7, - 0x6c80918a365ef78, + 0x06, 0xc8, 0x09, 0x18, 0xa3, 0x65, 0xef, 0x78, 0xae, 0x30, 0x74, 0x0c, 0x08, 0xa8, 0x75, + 0xd7, 0x90, 0x8a, 0x38, 0x7f, 0x48, 0x07, 0x35, 0xf1, 0x2b, 0x1f, 0x41, 0xab, 0x9f, 0x36, + 0xd6, 0x40, 0xac, 0x62, 0xa8, 0x2a, 0x8f, 0x51, 0xcd, 0x50, 0x4a, 0x49, 0xda, 0xd4, 0xff, + 0x6c, 0xde, 0x2d, ]); let mut a_fq = Fq::from_repr(a).unwrap(); let b = FqRepr([ - 0xbba57917c32f0cf0, - 0xe7f878cf87f05e5d, - 0x9498b4292fd27459, - 0xd59fd94ee4572cfa, - 0x1f607186d5bb0059, - 0xb13955f5ac7f6a3, + 0x0b, 0x13, 0x95, 0x5f, 0x5a, 0xc7, 0xf6, 0xa3, 0x1f, 0x60, 0x71, 0x86, 0xd5, 0xbb, 0x00, + 0x59, 0xd5, 0x9f, 0xd9, 0x4e, 0xe4, 0x57, 0x2c, 0xfa, 0x94, 0x98, 0xb4, 0x29, 0x2f, 0xd2, + 0x74, 0x59, 0xe7, 0xf8, 0x78, 0xcf, 0x87, 0xf0, 0x5e, 0x5d, 0xbb, 0xa5, 0x79, 0x17, 0xc3, + 0x2f, 0x0c, 0xf0, ]); let b_fq = Fq::from_repr(b).unwrap(); let c = FqRepr([ - 0xf5f70713b717914c, - 0x355ea5ac64cbbab1, - 0xce60dd43417ec960, - 0xf16b9d77b0ad7d10, - 0xa44c204c1de7cdb7, - 0x1684487772bc9a5a, + 0x16, 0x84, 0x48, 0x77, 0x72, 0xbc, 0x9a, 0x5a, 0xa4, 0x4c, 0x20, 0x4c, 0x1d, 0xe7, 0xcd, + 0xb7, 0xf1, 0x6b, 0x9d, 0x77, 0xb0, 0xad, 0x7d, 0x10, 0xce, 0x60, 0xdd, 0x43, 0x41, 0x7e, + 0xc9, 0x60, 0x35, 0x5e, 0xa5, 0xac, 0x64, 0xcb, 0xba, 0xb1, 0xf5, 0xf7, 0x07, 0x13, 0xb7, + 0x17, 0x91, 0x4c, ]); a_fq.mul_assign(&b_fq); assert_eq!(a_fq.into_repr(), c); // Zero should be in the field. - assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); + assert!(Fq::from_repr(FqRepr([0; 48])).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -2223,34 +1804,24 @@ fn test_fq_from_into_repr() { } } -#[test] -fn test_fq_repr_display() { - assert_eq!( - format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])), - "0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])), - "0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0, 0, 0, 0, 0, 0])), - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - #[test] fn test_fq_display() { assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()), + format!("{}", Fq::from_repr(FqRepr([ + 0x19, 0x47, 0xf0, 0xd5, 0xf4, 0xfe, 0x32, 0x5a, 0xb1, 0xd4, 0xaa, 0xd8, 0x76, 0x51, + 0xe6, 0x94, 0x67, 0x6c, 0xc4, 0xee, 0xf4, 0xc4, 0x6f, 0x2c, 0xb3, 0x8d, 0x35, 0xb3, + 0xf6, 0x77, 0x95, 0x85, 0x39, 0xa8, 0xf1, 0x84, 0xf3, 0x53, 0x5c, 0x7b, 0xa9, 0x56, + 0xba, 0xbf, 0x93, 0x01, 0xea, 0x24, + ])).unwrap()), "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() ); assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()), + format!("{}", Fq::from_repr(FqRepr([ + 0x06, 0xc9, 0xf5, 0xa1, 0x06, 0x0d, 0xe9, 0x74, 0x9a, 0x55, 0x18, 0x59, 0xb1, 0xe4, + 0x3a, 0x9a, 0xb7, 0xf8, 0x9f, 0x88, 0xf5, 0x9c, 0x1d, 0xc5, 0xa4, 0xb6, 0x2a, 0xf4, + 0xa7, 0x92, 0xa6, 0x89, 0x41, 0x3f, 0x6f, 0x7f, 0x06, 0xea, 0x87, 0xeb, 0xe2, 0x8e, + 0x79, 0x39, 0x6a, 0xc2, 0xbb, 0xf8, + ])).unwrap()), "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() ); } @@ -2304,8 +1875,7 @@ fn fq_field_tests() { #[test] fn test_fq_ordering() { - // FqRepr's ordering is well-tested, but we still need to make sure the Fq - // elements aren't being compared in Montgomery form. + // We need to make sure the Fq elements aren't being compared in Montgomery form. for i in 0..100 { assert!(Fq::from(i + 1) > Fq::from(i)); } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 6e6230736f..dd5b751f60 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -302,6 +302,11 @@ impl SqrtField for Fq2 { } } +#[cfg(test)] +use super::fq::FqRepr; +#[cfg(test)] +use ff::PrimeField; + #[test] fn test_fq2_ordering() { let mut a = Fq2 { @@ -353,9 +358,6 @@ fn test_fq2_basics() { #[test] fn test_fq2_squaring() { - use super::fq::FqRepr; - use ff::PrimeField; - let a = Fq2 { c0: Fq::one(), c1: Fq::one(), @@ -381,21 +383,17 @@ fn test_fq2_squaring() { let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x9c2c6309bbf8b598, - 0x4eef5c946536f602, - 0x90e34aab6fb6a6bd, - 0xf7f295a94e58ae7c, - 0x41b76dcc1c3fbe5e, - 0x7080c5fa1d8e042, + 0x07, 0x08, 0x0c, 0x5f, 0xa1, 0xd8, 0xe0, 0x42, 0x41, 0xb7, 0x6d, 0xcc, 0x1c, 0x3f, + 0xbe, 0x5e, 0xf7, 0xf2, 0x95, 0xa9, 0x4e, 0x58, 0xae, 0x7c, 0x90, 0xe3, 0x4a, 0xab, + 0x6f, 0xb6, 0xa6, 0xbd, 0x4e, 0xef, 0x5c, 0x94, 0x65, 0x36, 0xf6, 0x02, 0x9c, 0x2c, + 0x63, 0x09, 0xbb, 0xf8, 0xb5, 0x98, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x38f473b3c870a4ab, - 0x6ad3291177c8c7e5, - 0xdac5a4c911a4353e, - 0xbfb99020604137a0, - 0xfc58a7b7be815407, - 0x10d1615e75250a21, + 0x10, 0xd1, 0x61, 0x5e, 0x75, 0x25, 0x0a, 0x21, 0xfc, 0x58, 0xa7, 0xb7, 0xbe, 0x81, + 0x54, 0x07, 0xbf, 0xb9, 0x90, 0x20, 0x60, 0x41, 0x37, 0xa0, 0xda, 0xc5, 0xa4, 0xc9, + 0x11, 0xa4, 0x35, 0x3e, 0x6a, 0xd3, 0x29, 0x11, 0x77, 0xc8, 0xc7, 0xe5, 0x38, 0xf4, + 0x73, 0xb3, 0xc8, 0x70, 0xa4, 0xab, ])) .unwrap(), }; @@ -403,21 +401,17 @@ fn test_fq2_squaring() { a.square(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf262c28c538bcf68, - 0xb9f2a66eae1073ba, - 0xdc46ab8fad67ae0, - 0xcb674157618da176, - 0x4cf17b5893c3d327, - 0x7eac81369c43361 + 0x07, 0xea, 0xc8, 0x13, 0x69, 0xc4, 0x33, 0x61, 0x4c, 0xf1, 0x7b, 0x58, 0x93, 0xc3, + 0xd3, 0x27, 0xcb, 0x67, 0x41, 0x57, 0x61, 0x8d, 0xa1, 0x76, 0x0d, 0xc4, 0x6a, 0xb8, + 0xfa, 0xd6, 0x7a, 0xe0, 0xb9, 0xf2, 0xa6, 0x6e, 0xae, 0x10, 0x73, 0xba, 0xf2, 0x62, + 0xc2, 0x8c, 0x53, 0x8b, 0xcf, 0x68, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xc1579cf58e980cf8, - 0xa23eb7e12dd54d98, - 0xe75138bce4cec7aa, - 0x38d0d7275a9689e1, - 0x739c983042779a65, - 0x1542a61c8a8db994 + 0x15, 0x42, 0xa6, 0x1c, 0x8a, 0x8d, 0xb9, 0x94, 0x73, 0x9c, 0x98, 0x30, 0x42, 0x77, + 0x9a, 0x65, 0x38, 0xd0, 0xd7, 0x27, 0x5a, 0x96, 0x89, 0xe1, 0xe7, 0x51, 0x38, 0xbc, + 0xe4, 0xce, 0xc7, 0xaa, 0xa2, 0x3e, 0xb7, 0xe1, 0x2d, 0xd5, 0x4d, 0x98, 0xc1, 0x57, + 0x9c, 0xf5, 0x8e, 0x98, 0x0c, 0xf8, ])) .unwrap(), } @@ -431,41 +425,33 @@ fn test_fq2_mul() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x85c9f989e1461f03, - 0xa2e33c333449a1d6, - 0x41e461154a7354a3, - 0x9ee53e7e84d7532e, - 0x1c202d8ed97afb45, - 0x51d3f9253e2516f, + 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, + 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, + 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, + 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xa7348a8b511aedcf, - 0x143c215d8176b319, - 0x4cc48081c09b8903, - 0x9533e4a9a5158be, - 0x7a5e1ecb676d65f9, - 0x180c3ee46656b008, + 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, + 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, + 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, + 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, ])) .unwrap(), }; a.mul_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe21f9169805f537e, - 0xfc87e62e179c285d, - 0x27ece175be07a531, - 0xcd460f9f0c23e430, - 0x6c9110292bfa409, - 0x2c93a72eb8af83e, + 0x02, 0xc9, 0x3a, 0x72, 0xeb, 0x8a, 0xf8, 0x3e, 0x06, 0xc9, 0x11, 0x02, 0x92, 0xbf, + 0xa4, 0x09, 0xcd, 0x46, 0x0f, 0x9f, 0x0c, 0x23, 0xe4, 0x30, 0x27, 0xec, 0xe1, 0x75, + 0xbe, 0x07, 0xa5, 0x31, 0xfc, 0x87, 0xe6, 0x2e, 0x17, 0x9c, 0x28, 0x5d, 0xe2, 0x1f, + 0x91, 0x69, 0x80, 0x5f, 0x53, 0x7e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4b1c3f936d8992d4, - 0x1d2a72916dba4c8a, - 0x8871c508658d1e5f, - 0x57a06d3135a752ae, - 0x634cd3c6c565096d, - 0x19e17334d4e93558, + 0x19, 0xe1, 0x73, 0x34, 0xd4, 0xe9, 0x35, 0x58, 0x63, 0x4c, 0xd3, 0xc6, 0xc5, 0x65, + 0x09, 0x6d, 0x57, 0xa0, 0x6d, 0x31, 0x35, 0xa7, 0x52, 0xae, 0x88, 0x71, 0xc5, 0x08, + 0x65, 0x8d, 0x1e, 0x5f, 0x1d, 0x2a, 0x72, 0x91, 0x6d, 0xba, 0x4c, 0x8a, 0x4b, 0x1c, + 0x3f, 0x93, 0x6d, 0x89, 0x92, 0xd4, ])) .unwrap(), }); @@ -473,21 +459,17 @@ fn test_fq2_mul() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x95b5127e6360c7e4, - 0xde29c31a19a6937e, - 0xf61a96dacf5a39bc, - 0x5511fe4d84ee5f78, - 0x5310a202d92f9963, - 0x1751afbe166e5399 + 0x17, 0x51, 0xaf, 0xbe, 0x16, 0x6e, 0x53, 0x99, 0x53, 0x10, 0xa2, 0x02, 0xd9, 0x2f, + 0x99, 0x63, 0x55, 0x11, 0xfe, 0x4d, 0x84, 0xee, 0x5f, 0x78, 0xf6, 0x1a, 0x96, 0xda, + 0xcf, 0x5a, 0x39, 0xbc, 0xde, 0x29, 0xc3, 0x1a, 0x19, 0xa6, 0x93, 0x7e, 0x95, 0xb5, + 0x12, 0x7e, 0x63, 0x60, 0xc7, 0xe4, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x84af0e1bd630117a, - 0x6c63cd4da2c2aa7, - 0x5ba6e5430e883d40, - 0xc975106579c275ee, - 0x33a9ac82ce4c5083, - 0x1ef1a36c201589d + 0x01, 0xef, 0x1a, 0x36, 0xc2, 0x01, 0x58, 0x9d, 0x33, 0xa9, 0xac, 0x82, 0xce, 0x4c, + 0x50, 0x83, 0xc9, 0x75, 0x10, 0x65, 0x79, 0xc2, 0x75, 0xee, 0x5b, 0xa6, 0xe5, 0x43, + 0x0e, 0x88, 0x3d, 0x40, 0x06, 0xc6, 0x3c, 0xd4, 0xda, 0x2c, 0x2a, 0xa7, 0x84, 0xaf, + 0x0e, 0x1b, 0xd6, 0x30, 0x11, 0x7a, ])) .unwrap(), } @@ -503,21 +485,17 @@ fn test_fq2_invert() { let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x85c9f989e1461f03, - 0xa2e33c333449a1d6, - 0x41e461154a7354a3, - 0x9ee53e7e84d7532e, - 0x1c202d8ed97afb45, - 0x51d3f9253e2516f, + 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, + 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, + 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, + 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xa7348a8b511aedcf, - 0x143c215d8176b319, - 0x4cc48081c09b8903, - 0x9533e4a9a5158be, - 0x7a5e1ecb676d65f9, - 0x180c3ee46656b008, + 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, + 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, + 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, + 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, ])) .unwrap(), }; @@ -526,21 +504,17 @@ fn test_fq2_invert() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x70300f9bcb9e594, - 0xe5ecda5fdafddbb2, - 0x64bef617d2915a8f, - 0xdfba703293941c30, - 0xa6c3d8f9586f2636, - 0x1351ef01941b70c4 + 0x13, 0x51, 0xef, 0x01, 0x94, 0x1b, 0x70, 0xc4, 0xa6, 0xc3, 0xd8, 0xf9, 0x58, 0x6f, + 0x26, 0x36, 0xdf, 0xba, 0x70, 0x32, 0x93, 0x94, 0x1c, 0x30, 0x64, 0xbe, 0xf6, 0x17, + 0xd2, 0x91, 0x5a, 0x8f, 0xe5, 0xec, 0xda, 0x5f, 0xda, 0xfd, 0xdb, 0xb2, 0x07, 0x03, + 0x00, 0xf9, 0xbc, 0xb9, 0xe5, 0x94, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x8c39fd76a8312cb4, - 0x15d7b6b95defbff0, - 0x947143f89faedee9, - 0xcbf651a0f367afb2, - 0xdf4e54f0d3ef15a6, - 0x103bdf241afb0019 + 0x10, 0x3b, 0xdf, 0x24, 0x1a, 0xfb, 0x00, 0x19, 0xdf, 0x4e, 0x54, 0xf0, 0xd3, 0xef, + 0x15, 0xa6, 0xcb, 0xf6, 0x51, 0xa0, 0xf3, 0x67, 0xaf, 0xb2, 0x94, 0x71, 0x43, 0xf8, + 0x9f, 0xae, 0xde, 0xe9, 0x15, 0xd7, 0xb6, 0xb9, 0x5d, 0xef, 0xbf, 0xf0, 0x8c, 0x39, + 0xfd, 0x76, 0xa8, 0x31, 0x2c, 0xb4, ])) .unwrap(), } @@ -554,41 +528,33 @@ fn test_fq2_addition() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; a.add_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0x619a02d78dc70ef2, - 0xb93adfc9119e33e8, - 0x4bf0b99a9f0dca12, - 0x3b88899a42a6318f, - 0x986a4a62fa82a49d, - 0x13ce433fa26027f5, + 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, + 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, + 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, + 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x66323bf80b58b9b9, - 0xa1379b6facf6e596, - 0x402aef1fb797e32f, - 0x2236f55246d0d44d, - 0x4c8c1800eb104566, - 0x11d6e20e986c2085, + 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, + 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, + 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, + 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, ])) .unwrap(), }); @@ -596,21 +562,17 @@ fn test_fq2_addition() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8e9a7adaf6eb0eb9, - 0xcb207e6b3341eaba, - 0xd70b0c7b481d23ff, - 0xf4ef57d604b6bca2, - 0x65309427b3d5d090, - 0x14c715d5553f01d2 + 0x14, 0xc7, 0x15, 0xd5, 0x55, 0x3f, 0x01, 0xd2, 0x65, 0x30, 0x94, 0x27, 0xb3, 0xd5, + 0xd0, 0x90, 0xf4, 0xef, 0x57, 0xd6, 0x04, 0xb6, 0xbc, 0xa2, 0xd7, 0x0b, 0x0c, 0x7b, + 0x48, 0x1d, 0x23, 0xff, 0xcb, 0x20, 0x7e, 0x6b, 0x33, 0x41, 0xea, 0xba, 0x8e, 0x9a, + 0x7a, 0xda, 0xf6, 0xeb, 0x0e, 0xb9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xfdb032e7d9079a94, - 0x35a2809d15468d83, - 0xfe4b23317e0796d5, - 0xd62fa51334f560fa, - 0x9ad265eb46e01984, - 0x1303f3465112c8bc + 0x13, 0x03, 0xf3, 0x46, 0x51, 0x12, 0xc8, 0xbc, 0x9a, 0xd2, 0x65, 0xeb, 0x46, 0xe0, + 0x19, 0x84, 0xd6, 0x2f, 0xa5, 0x13, 0x34, 0xf5, 0x60, 0xfa, 0xfe, 0x4b, 0x23, 0x31, + 0x7e, 0x07, 0x96, 0xd5, 0x35, 0xa2, 0x80, 0x9d, 0x15, 0x46, 0x8d, 0x83, 0xfd, 0xb0, + 0x32, 0xe7, 0xd9, 0x07, 0x9a, 0x94, ])) .unwrap(), } @@ -624,41 +586,33 @@ fn test_fq2_subtraction() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; a.sub_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0x619a02d78dc70ef2, - 0xb93adfc9119e33e8, - 0x4bf0b99a9f0dca12, - 0x3b88899a42a6318f, - 0x986a4a62fa82a49d, - 0x13ce433fa26027f5, + 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, + 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, + 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, + 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x66323bf80b58b9b9, - 0xa1379b6facf6e596, - 0x402aef1fb797e32f, - 0x2236f55246d0d44d, - 0x4c8c1800eb104566, - 0x11d6e20e986c2085, + 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, + 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, + 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, + 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, ])) .unwrap(), }); @@ -666,21 +620,17 @@ fn test_fq2_subtraction() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8565752bdb5c9b80, - 0x7756bed7c15982e9, - 0xa65a6be700b285fe, - 0xe255902672ef6c43, - 0x7f77a718021c342d, - 0x72ba14049fe9881 + 0x07, 0x2b, 0xa1, 0x40, 0x49, 0xfe, 0x98, 0x81, 0x7f, 0x77, 0xa7, 0x18, 0x02, 0x1c, + 0x34, 0x2d, 0xe2, 0x55, 0x90, 0x26, 0x72, 0xef, 0x6c, 0x43, 0xa6, 0x5a, 0x6b, 0xe7, + 0x00, 0xb2, 0x85, 0xfe, 0x77, 0x56, 0xbe, 0xd7, 0xc1, 0x59, 0x82, 0xe9, 0x85, 0x65, + 0x75, 0x2b, 0xdb, 0x5c, 0x9b, 0x80, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xeb4abaf7c255d1cd, - 0x11df49bc6cacc256, - 0xe52617930588c69a, - 0xf63905f39ad8cb1f, - 0x4cd5dd9fb40b3b8f, - 0x957411359ba6e4c + 0x09, 0x57, 0x41, 0x13, 0x59, 0xba, 0x6e, 0x4c, 0x4c, 0xd5, 0xdd, 0x9f, 0xb4, 0x0b, + 0x3b, 0x8f, 0xf6, 0x39, 0x05, 0xf3, 0x9a, 0xd8, 0xcb, 0x1f, 0xe5, 0x26, 0x17, 0x93, + 0x05, 0x88, 0xc6, 0x9a, 0x11, 0xdf, 0x49, 0xbc, 0x6c, 0xac, 0xc2, 0x56, 0xeb, 0x4a, + 0xba, 0xf7, 0xc2, 0x55, 0xd1, 0xcd, ])) .unwrap(), } @@ -694,21 +644,17 @@ fn test_fq2_negation() { let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -717,21 +663,17 @@ fn test_fq2_negation() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8cfe87fc96dbaae4, - 0xcc6615c8fb0492d, - 0xdc167fc04da19c37, - 0xab107d49317487ab, - 0x7e555df189f880e3, - 0x19083f5486a10cbd + 0x19, 0x08, 0x3f, 0x54, 0x86, 0xa1, 0x0c, 0xbd, 0x7e, 0x55, 0x5d, 0xf1, 0x89, 0xf8, + 0x80, 0xe3, 0xab, 0x10, 0x7d, 0x49, 0x31, 0x74, 0x87, 0xab, 0xdc, 0x16, 0x7f, 0xc0, + 0x4d, 0xa1, 0x9c, 0x37, 0x0c, 0xc6, 0x61, 0x5c, 0x8f, 0xb0, 0x49, 0x2d, 0x8c, 0xfe, + 0x87, 0xfc, 0x96, 0xdb, 0xaa, 0xe4, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x228109103250c9d0, - 0x8a411ad149045812, - 0xa9109e8f3041427e, - 0xb07e9bc405608611, - 0xfcd559cbe77bd8b8, - 0x18d400b280d93e62 + 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, + 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, + 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, + 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, ])) .unwrap(), } @@ -745,21 +687,17 @@ fn test_fq2_doubling() { let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; @@ -767,21 +705,17 @@ fn test_fq2_doubling() { a.double(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0x5a00f006d247ff8e, - 0x23cb3d4443476da4, - 0x1634a5c1521eb3da, - 0x72cd9c7784211627, - 0x998c938972a657e7, - 0x1f1a52b65bdb3b9 + 0x01, 0xf1, 0xa5, 0x2b, 0x65, 0xbd, 0xb3, 0xb9, 0x99, 0x8c, 0x93, 0x89, 0x72, 0xa6, + 0x57, 0xe7, 0x72, 0xcd, 0x9c, 0x77, 0x84, 0x21, 0x16, 0x27, 0x16, 0x34, 0xa5, 0xc1, + 0x52, 0x1e, 0xb3, 0xda, 0x23, 0xcb, 0x3d, 0x44, 0x43, 0x47, 0x6d, 0xa4, 0x5a, 0x00, + 0xf0, 0x06, 0xd2, 0x47, 0xff, 0x8e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x2efbeddf9b5dc1b6, - 0x28d5ca5ad09f4fdb, - 0x7c4068238cdf674b, - 0x67f15f81dc49195b, - 0x9c8c9bd4b79fa83d, - 0x25a226f714d506e + 0x02, 0x5a, 0x22, 0x6f, 0x71, 0x4d, 0x50, 0x6e, 0x9c, 0x8c, 0x9b, 0xd4, 0xb7, 0x9f, + 0xa8, 0x3d, 0x67, 0xf1, 0x5f, 0x81, 0xdc, 0x49, 0x19, 0x5b, 0x7c, 0x40, 0x68, 0x23, + 0x8c, 0xdf, 0x67, 0x4b, 0x28, 0xd5, 0xca, 0x5a, 0xd0, 0x9f, 0x4f, 0xdb, 0x2e, 0xfb, + 0xed, 0xdf, 0x9b, 0x5d, 0xc1, 0xb6, ])) .unwrap(), } @@ -795,21 +729,17 @@ fn test_fq2_frobenius_map() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; @@ -818,21 +748,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -842,21 +768,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x228109103250c9d0, - 0x8a411ad149045812, - 0xa9109e8f3041427e, - 0xb07e9bc405608611, - 0xfcd559cbe77bd8b8, - 0x18d400b280d93e62 + 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, + 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, + 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, + 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, ])) .unwrap(), } @@ -866,21 +788,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -890,21 +808,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -919,21 +833,17 @@ fn test_fq2_sqrt() { assert_eq!( Fq2 { c0: Fq::from_repr(FqRepr([ - 0x476b4c309720e227, - 0x34c2d04faffdab6, - 0xa57e6fc1bab51fd9, - 0xdb4a116b5bf74aa1, - 0x1e58b2159dfe10e2, - 0x7ca7da1f13606ac + 0x07, 0xca, 0x7d, 0xa1, 0xf1, 0x36, 0x06, 0xac, 0x1e, 0x58, 0xb2, 0x15, 0x9d, 0xfe, + 0x10, 0xe2, 0xdb, 0x4a, 0x11, 0x6b, 0x5b, 0xf7, 0x4a, 0xa1, 0xa5, 0x7e, 0x6f, 0xc1, + 0xba, 0xb5, 0x1f, 0xd9, 0x03, 0x4c, 0x2d, 0x04, 0xfa, 0xff, 0xda, 0xb6, 0x47, 0x6b, + 0x4c, 0x30, 0x97, 0x20, 0xe2, 0x27, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xfa8de88b7516d2c3, - 0x371a75ed14f41629, - 0x4cec2dca577a3eb6, - 0x212611bca4e99121, - 0x8ee5394d77afb3d, - 0xec92336650e49d5 + 0x0e, 0xc9, 0x23, 0x36, 0x65, 0x0e, 0x49, 0xd5, 0x08, 0xee, 0x53, 0x94, 0xd7, 0x7a, + 0xfb, 0x3d, 0x21, 0x26, 0x11, 0xbc, 0xa4, 0xe9, 0x91, 0x21, 0x4c, 0xec, 0x2d, 0xca, + 0x57, 0x7a, 0x3e, 0xb6, 0x37, 0x1a, 0x75, 0xed, 0x14, 0xf4, 0x16, 0x29, 0xfa, 0x8d, + 0xe8, 0x8b, 0x75, 0x16, 0xd2, 0xc3, ])) .unwrap(), } @@ -941,21 +851,17 @@ fn test_fq2_sqrt() { .unwrap(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0x40b299b2704258c5, - 0x6ef7de92e8c68b63, - 0x6d2ddbe552203e82, - 0x8d7f1f723d02c1d3, - 0x881b3e01b611c070, - 0x10f6963bbad2ebc5 + 0x10, 0xf6, 0x96, 0x3b, 0xba, 0xd2, 0xeb, 0xc5, 0x88, 0x1b, 0x3e, 0x01, 0xb6, 0x11, + 0xc0, 0x70, 0x8d, 0x7f, 0x1f, 0x72, 0x3d, 0x02, 0xc1, 0xd3, 0x6d, 0x2d, 0xdb, 0xe5, + 0x52, 0x20, 0x3e, 0x82, 0x6e, 0xf7, 0xde, 0x92, 0xe8, 0xc6, 0x8b, 0x63, 0x40, 0xb2, + 0x99, 0xb2, 0x70, 0x42, 0x58, 0xc5, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xc099534fc209e752, - 0x7670594665676447, - 0x28a20faed211efe7, - 0x6b852aeaf2afcb1b, - 0xa4c93b08105d71a9, - 0x8d7cfff94216330 + 0x08, 0xd7, 0xcf, 0xff, 0x94, 0x21, 0x63, 0x30, 0xa4, 0xc9, 0x3b, 0x08, 0x10, 0x5d, + 0x71, 0xa9, 0x6b, 0x85, 0x2a, 0xea, 0xf2, 0xaf, 0xcb, 0x1b, 0x28, 0xa2, 0x0f, 0xae, + 0xd2, 0x11, 0xef, 0xe7, 0x76, 0x70, 0x59, 0x46, 0x65, 0x67, 0x64, 0x47, 0xc0, 0x99, + 0x53, 0x4f, 0xc2, 0x09, 0xe7, 0x52, ])) .unwrap(), } @@ -964,12 +870,10 @@ fn test_fq2_sqrt() { assert_eq!( Fq2 { c0: Fq::from_repr(FqRepr([ - 0xb9f78429d1517a6b, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, + 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, + 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xf7, + 0x84, 0x29, 0xd1, 0x51, 0x7a, 0x6b, ])) .unwrap(), c1: Fq::zero(), @@ -979,12 +883,10 @@ fn test_fq2_sqrt() { Fq2 { c0: Fq::zero(), c1: Fq::from_repr(FqRepr([ - 0xb9fefffffd4357a3, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, + 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, + 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, + 0xff, 0xff, 0xfd, 0x43, 0x57, 0xa3, ])) .unwrap(), } diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 6bfb17520f..4a153ad32c 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -1,10 +1,11 @@ -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use std::ops::{AddAssign, MulAssign, SubAssign}; #[derive(PrimeField)] #[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] #[PrimeFieldGenerator = "7"] -pub struct Fr(FrRepr); +#[PrimeFieldReprEndianness = "little"] +pub struct Fr([u64; 4]); #[cfg(test)] use ff::PowVartime; @@ -15,373 +16,26 @@ use rand_xorshift::XorShiftRng; #[cfg(test)] use std::ops::Neg; -#[test] -fn test_fr_repr_ordering() { - fn assert_equality(a: FrRepr, b: FrRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); - } - - fn assert_lt(a: FrRepr, b: FrRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FrRepr([9999, 9999, 9999, 9999]), - FrRepr([9999, 9999, 9999, 9999]), - ); - assert_equality( - FrRepr([9999, 9998, 9999, 9999]), - FrRepr([9999, 9998, 9999, 9999]), - ); - assert_equality( - FrRepr([9999, 9999, 9999, 9997]), - FrRepr([9999, 9999, 9999, 9997]), - ); - assert_lt( - FrRepr([9999, 9997, 9999, 9998]), - FrRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FrRepr([9999, 9997, 9998, 9999]), - FrRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FrRepr([9, 9999, 9999, 9997]), - FrRepr([9999, 9999, 9999, 9997]), - ); -} - -#[test] -fn test_fr_repr_from() { - assert_eq!(FrRepr::from(100), FrRepr([100, 0, 0, 0])); -} - -#[test] -fn test_fr_repr_is_odd() { - assert!(!FrRepr::from(0).is_odd()); - assert!(FrRepr::from(0).is_even()); - assert!(FrRepr::from(1).is_odd()); - assert!(!FrRepr::from(1).is_even()); - assert!(!FrRepr::from(324834872).is_odd()); - assert!(FrRepr::from(324834872).is_even()); - assert!(FrRepr::from(324834873).is_odd()); - assert!(!FrRepr::from(324834873).is_even()); -} - -#[test] -fn test_fr_repr_is_zero() { - assert!(FrRepr::from(0).is_zero()); - assert!(!FrRepr::from(1).is_zero()); - assert!(!FrRepr([0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fr_repr_div2() { - let mut a = FrRepr([ - 0xbd2920b19c972321, - 0x174ed0466a3be37e, - 0xd468d5e3b551f0b5, - 0xcb67c072733beefc, - ]); - a.div2(); - assert_eq!( - a, - FrRepr([ - 0x5e949058ce4b9190, - 0x8ba76823351df1bf, - 0x6a346af1daa8f85a, - 0x65b3e039399df77e - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FrRepr([ - 0x6fd7a524163392e4, - 0x16a2e9da08cd477c, - 0xdf9a8d1abc76aa3e, - 0x196cf80e4e677d - ]) - ); - for _ in 0..200 { - a.div2(); - } - assert_eq!(a, FrRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); - for _ in 0..40 { - a.div2(); - } - assert_eq!(a, FrRepr([0x19, 0x0, 0x0, 0x0])); - for _ in 0..4 { - a.div2(); - } - assert_eq!(a, FrRepr([0x1, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fr_repr_shr() { - let mut a = FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, - ]); - a.shr(0); - assert_eq!( - a, - FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1 - ]) - ); - a.shr(1); - assert_eq!( - a, - FrRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0 - ]) - ); - a.shr(50); - assert_eq!( - a, - FrRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x75611bce1b4335e, - 0x6c0 - ]) - ); - a.shr(130); - assert_eq!(a, FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); - a.shr(64); - assert_eq!(a, FrRepr([0x1b0, 0x0, 0x0, 0x0])); -} - -#[test] -fn test_fr_repr_mul2() { - let mut a = FrRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FrRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); - for _ in 0..128 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x0, 0x0, 0x0, 0x9600000000000000])); - for _ in 0..7 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fr_repr_num_bits() { - let mut a = FrRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FrRepr::from(1); - for i in 1..257 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fr_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FrRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ]); - t.sub_noborrow(&FrRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ])); - assert!( - t == FrRepr([ - 0xb813415048991c1f, - 0x10ad07ae88725d92, - 0x5a7b851271759961, - 0x36850eedd30c39c5 - ]) - ); - - for _ in 0..1000 { - let mut a = Fr::random(&mut rng).into_repr(); - a.0[3] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } - - // Subtracting r+1 from r should produce -1 (mod 2**256) - let mut qplusone = FrRepr([ - 0xffffffff00000001, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ]); - qplusone.sub_noborrow(&FrRepr([ - 0xffffffff00000002, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ])); - assert_eq!( - qplusone, - FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ); -} - -#[test] -fn test_fr_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FrRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ]); - t.add_nocarry(&FrRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ])); - assert_eq!( - t, - FrRepr([ - 0x64b20e805c30a967, - 0x59a9ee9aa114a02, - 0x5871a104789020c9, - 0x8999707c5c726f65 - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fr::random(&mut rng).into_repr(); - let mut b = Fr::random(&mut rng).into_repr(); - let mut c = Fr::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[3] >>= 3; - b.0[3] >>= 3; - c.0[3] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } - - // Adding 1 to (2^256 - 1) should produce zero - let mut x = FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - x.add_nocarry(&FrRepr::from(1)); - assert!(x.is_zero()); -} - #[test] fn test_fr_is_valid() { - let mut a = Fr(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FrRepr::from(1)); + a.sub_noborrow(&Fr([1, 0, 0, 0])); assert!(a.is_valid()); assert!(Fr::from(0).is_valid()); - assert!(Fr(FrRepr([ + assert!(Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 - ])) + ]) .is_valid()); - assert!(!Fr(FrRepr([ + assert!(!Fr([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -399,85 +53,85 @@ fn test_fr_is_valid() { fn test_fr_add_assign() { { // Random number - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca, - ])); + ]); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fr(FrRepr::from(0))); + tmp.add_assign(&Fr([0, 0, 0, 0])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fr(FrRepr::from(1))); + tmp.add_assign(&Fr([1, 0, 0, 0])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580766, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fr(FrRepr([ + tmp.add_assign(&Fr([ 0x946f435944f7dc79, 0xb55e7ee6533a9b9b, 0x1e43b84c2f6194ca, 0x58717ab525463496, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xd7ec2abbb24fe3de, 0x35cdf7ae7d0d62f7, 0xd899557c477cd0e9, 0x3371b52bc43de018 - ])) + ]) ); // Add one to (r - 1) and test for the result. - tmp = Fr(FrRepr([ + tmp = Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48, - ])); - tmp.add_assign(&Fr(FrRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fr([1, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is r - 1 - tmp = Fr(FrRepr([ + tmp = Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ])); - tmp.add_assign(&Fr(FrRepr([ + ]); + tmp.add_assign(&Fr([ 0x521a525223349e70, 0xa99bb5f3d8231f31, 0xde8e397bebe477e, 0x1ad08e5041d7c321, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fr(FrRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fr([1, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -511,71 +165,71 @@ fn test_fr_add_assign() { fn test_fr_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c, - ])); - tmp.sub_assign(&Fr(FrRepr([ + ]); + tmp.sub_assign(&Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xbc83189d92a7f89c, 0x7f908737d62d38a3, 0x45aa62cfe7e4c3e1, 0x24ffc5896108547d - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fr(FrRepr([ + tmp = Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ])); - tmp.sub_assign(&Fr(FrRepr([ + ]); + tmp.sub_assign(&Fr([ 0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Test for sensible results with zero - tmp = Fr(FrRepr::from(0)); - tmp.sub_assign(&Fr(FrRepr::from(0))); + tmp = Fr::from(0); + tmp.sub_assign(&Fr::from(0)); assert!(tmp.is_zero()); - tmp = Fr(FrRepr([ + tmp = Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca, - ])); - tmp.sub_assign(&Fr(FrRepr::from(0))); + ]); + tmp.sub_assign(&Fr::from(0)); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); } @@ -602,25 +256,25 @@ fn test_fr_sub_assign() { #[test] fn test_fr_mul_assign() { - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x6b7e9b8faeefc81a, 0xe30a8463f348ba42, 0xeff3cb67a8279c9c, 0x3d303651bd7c774d, - ])); - tmp.mul_assign(&Fr(FrRepr([ + ]); + tmp.mul_assign(&Fr([ 0x13ae28e3bc35ebeb, 0xa10f4488075cae2c, 0x8160e95a853c3b5d, 0x5ae3f03b561a841d, - ]))); + ])); assert!( - tmp == Fr(FrRepr([ + tmp == Fr([ 0x23717213ce710f71, 0xdbee1fe53a16e1af, 0xf565d3e1c2a48000, 0x4426507ee75df9d7 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -672,80 +326,73 @@ fn test_fr_mul_assign() { #[test] fn test_fr_shr() { let mut a = Fr::from_repr(FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, + 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, 0x7d, + 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, 0xb0, 0x3a, + 0x00, 0x36, ])) .unwrap(); a = a >> 0; assert_eq!( a.into_repr(), FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, + 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, + 0x7d, 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, + 0xb0, 0x3a, 0x00, 0x36, ]) ); a = a >> 1; assert_eq!( a.into_repr(), FrRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0, + 0x1f, 0x14, 0x15, 0x24, 0x76, 0xdd, 0x9f, 0xd9, 0xef, 0x59, 0x46, 0xd4, 0x69, 0xf0, + 0xbe, 0xcc, 0x92, 0x2a, 0x07, 0x4d, 0x95, 0x16, 0x7b, 0xcd, 0xd0, 0x86, 0xf3, 0x46, + 0x58, 0x1d, 0x00, 0x1b, ]) ); a = a >> 50; assert_eq!( a.into_repr(), FrRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x075611bce1b4335e, - 0x00000000000006c0, + 0x67, 0xf6, 0x7b, 0x96, 0x11, 0x75, 0x1a, 0xbc, 0x2f, 0xb3, 0xa4, 0xca, 0x41, 0x53, + 0xa5, 0xc5, 0x5e, 0x33, 0xb4, 0xe1, 0xbc, 0x11, 0x56, 0x07, 0xc0, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); a = a >> 130; assert_eq!( a.into_repr(), FrRepr([ - 0x01d5846f386d0cd7, - 0x00000000000001b0, - 0x0000000000000000, - 0x0000000000000000, + 0xd7, 0x0c, 0x6d, 0x38, 0x6f, 0x84, 0xd5, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); a = a >> 64; assert_eq!( a.into_repr(), FrRepr([ - 0x00000000000001b0, - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, + 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); } #[test] fn test_fr_squaring() { - let a = Fr(FrRepr([ + let a = Fr([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x73eda753299d7d47, - ])); + ]); assert!(a.is_valid()); assert_eq!( a.square(), Fr::from_repr(FrRepr([ - 0xc0d698e7bde077b8, - 0xb79a310579e76ec2, - 0xac1da8d0a9af4e5f, - 0x13f629c49bf23e97 + 0xb8, 0x77, 0xe0, 0xbd, 0xe7, 0x98, 0xd6, 0xc0, 0xc2, 0x6e, 0xe7, 0x79, 0x05, 0x31, + 0x9a, 0xb7, 0x5f, 0x4e, 0xaf, 0xa9, 0xd0, 0xa8, 0x1d, 0xac, 0x97, 0x3e, 0xf2, 0x9b, + 0xc4, 0x29, 0xf6, 0x13, ])) .unwrap() ); @@ -883,42 +530,38 @@ fn test_fr_sqrt() { fn test_fr_from_into_repr() { // r + 1 should not be in the field assert!(Fr::from_repr(FrRepr([ - 0xffffffff00000002, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48 + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, + 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, + 0xed, 0x73, ])) - .is_err()); + .is_none()); // r should not be in the field - assert!(Fr::from_repr(Fr::char()).is_err()); + assert!(Fr::from_repr(Fr::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. let a = FrRepr([ - 0x25ebe3a3ad3c0c6a, - 0x6990e39d092e817c, - 0x941f900d42f5658e, - 0x44f8a103b38a71e0, + 0x6a, 0x0c, 0x3c, 0xad, 0xa3, 0xe3, 0xeb, 0x25, 0x7c, 0x81, 0x2e, 0x09, 0x9d, 0xe3, 0x90, + 0x69, 0x8e, 0x65, 0xf5, 0x42, 0x0d, 0x90, 0x1f, 0x94, 0xe0, 0x71, 0x8a, 0xb3, 0x03, 0xa1, + 0xf8, 0x44, ]); let mut a_fr = Fr::from_repr(a).unwrap(); let b = FrRepr([ - 0x264e9454885e2475, - 0x46f7746bb0308370, - 0x4683ef5347411f9, - 0x58838d7f208d4492, + 0x75, 0x24, 0x5e, 0x88, 0x54, 0x94, 0x4e, 0x26, 0x70, 0x83, 0x30, 0xb0, 0x6b, 0x74, 0xf7, + 0x46, 0xf9, 0x11, 0x74, 0x34, 0xf5, 0x3e, 0x68, 0x04, 0x92, 0x44, 0x8d, 0x20, 0x7f, 0x8d, + 0x83, 0x58, ]); let b_fr = Fr::from_repr(b).unwrap(); let c = FrRepr([ - 0x48a09ab93cfc740d, - 0x3a6600fbfc7a671, - 0x838567017501d767, - 0x7161d6da77745512, + 0x0d, 0x74, 0xfc, 0x3c, 0xb9, 0x9a, 0xa0, 0x48, 0x71, 0xa6, 0xc7, 0xbf, 0x0f, 0x60, 0xa6, + 0x03, 0x67, 0xd7, 0x01, 0x75, 0x01, 0x67, 0x85, 0x83, 0x12, 0x55, 0x74, 0x77, 0xda, 0xd6, + 0x61, 0x71, ]); a_fr.mul_assign(&b_fr); assert_eq!(a_fr.into_repr(), c); // Zero should be in the field. - assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); + assert!(Fr::from_repr(FrRepr([0; 32])).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -937,60 +580,15 @@ fn test_fr_from_into_repr() { } } -#[test] -fn test_fr_repr_display() { - assert_eq!( - format!( - "{}", - FrRepr([ - 0x2829c242fa826143, - 0x1f32cf4dd4330917, - 0x932e4e479d168cd9, - 0x513c77587f563f64 - ]) - ), - "0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string() - ); - assert_eq!( - format!( - "{}", - FrRepr([ - 0x25ebe3a3ad3c0c6a, - 0x6990e39d092e817c, - 0x941f900d42f5658e, - 0x44f8a103b38a71e0 - ]) - ), - "0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string() - ); - assert_eq!( - format!( - "{}", - FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FrRepr([0, 0, 0, 0])), - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - #[test] fn test_fr_display() { assert_eq!( format!( "{}", Fr::from_repr(FrRepr([ - 0xc3cae746a3b5ecc7, - 0x185ec8eb3f5b5aee, - 0x684499ffe4b9dd99, - 0x7c9bba7afb68faa + 0xc7, 0xec, 0xb5, 0xa3, 0x46, 0xe7, 0xca, 0xc3, 0xee, 0x5a, 0x5b, 0x3f, 0xeb, 0xc8, + 0x5e, 0x18, 0x99, 0xdd, 0xb9, 0xe4, 0xff, 0x99, 0x44, 0x68, 0xaa, 0x8f, 0xb6, 0xaf, + 0xa7, 0xbb, 0xc9, 0x07, ])) .unwrap() ), @@ -1000,10 +598,9 @@ fn test_fr_display() { format!( "{}", Fr::from_repr(FrRepr([ - 0x44c71298ff198106, - 0xb0ad10817df79b6a, - 0xd034a80a2b74132b, - 0x41cf9a1336f50719 + 0x06, 0x81, 0x19, 0xff, 0x98, 0x12, 0xc7, 0x44, 0x6a, 0x9b, 0xf7, 0x7d, 0x81, 0x10, + 0xad, 0xb0, 0x2b, 0x13, 0x74, 0x2b, 0x0a, 0xa8, 0x34, 0xd0, 0x19, 0x07, 0xf5, 0x36, + 0x13, 0x9a, 0xcf, 0x41, ])) .unwrap() ), diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index f79961cdbc..6d9252e83a 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,4 +1,4 @@ -use ff::PrimeFieldRepr; +use ff::PrimeField; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use super::*; @@ -147,13 +147,15 @@ fn test_g1_uncompressed_invalid_vectors() { } } - let m = Fq::char(); + // PrimeField::char() returns the modulus in its little-endian byte representation, + // but Fq field elements use big-endian encoding, so flip the endianness. + let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate"); } else { panic!("should have rejected the point") @@ -162,9 +164,9 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate"); } else { panic!("should have rejected the point") @@ -175,7 +177,7 @@ fn test_g1_uncompressed_invalid_vectors() { let m = Fq::zero().into_repr(); let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { // :) @@ -198,8 +200,8 @@ fn test_g1_uncompressed_invalid_vectors() { let y = y.unwrap(); // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.into_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(y.into_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -263,13 +265,15 @@ fn test_g2_uncompressed_invalid_vectors() { } } - let m = Fq::char(); + // PrimeField::char() returns the modulus in its little-endian byte representation, + // but Fq field elements use big-endian encoding, so flip the endianness. + let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c1)"); } else { panic!("should have rejected the point") @@ -278,9 +282,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..96].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c0)"); } else { panic!("should have rejected the point") @@ -289,9 +293,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[96..]).unwrap(); + o.as_mut()[96..144].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c1)"); } else { panic!("should have rejected the point") @@ -300,9 +304,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[144..]).unwrap(); + o.as_mut()[144..].copy_from_slice(&m[..]); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c0)"); } else { panic!("should have rejected the point") @@ -313,8 +317,8 @@ fn test_g2_uncompressed_invalid_vectors() { let m = Fq::zero().into_repr(); let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { // :) @@ -340,10 +344,10 @@ fn test_g2_uncompressed_invalid_vectors() { let y = y.unwrap(); // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); - y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap(); - y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); + o.as_mut()[48..96].copy_from_slice(x.c0.into_repr().as_ref()); + o.as_mut()[96..144].copy_from_slice(y.c1.into_repr().as_ref()); + o.as_mut()[144..].copy_from_slice(y.c0.into_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -407,14 +411,16 @@ fn test_g1_compressed_invalid_vectors() { } } - let m = Fq::char(); + // PrimeField::char() returns the modulus in its little-endian byte representation, + // but Fq field elements use big-endian encoding, so flip the endianness. + let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(&m[..]); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate"); } else { panic!("should have rejected the point") @@ -433,7 +439,7 @@ fn test_g1_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { x.add_assign(&Fq::one()); } else { - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut().copy_from_slice(x.into_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -456,7 +462,7 @@ fn test_g1_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut().copy_from_slice(x.into_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { @@ -521,14 +527,16 @@ fn test_g2_compressed_invalid_vectors() { } } - let m = Fq::char(); + // PrimeField::char() returns the modulus in its little-endian byte representation, + // but Fq field elements use big-endian encoding, so flip the endianness. + let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(&m[..]); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c1)"); } else { panic!("should have rejected the point") @@ -537,10 +545,10 @@ fn test_g2_compressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..96].copy_from_slice(&m[..]); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c0)"); } else { panic!("should have rejected the point") @@ -565,8 +573,8 @@ fn test_g2_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { x.add_assign(&Fq2::one()); } else { - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.into_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -595,8 +603,8 @@ fn test_g2_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.into_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index 64242322e1..0288987ee6 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -2,7 +2,7 @@ use ff::{Field, PowVartime, PrimeField, SqrtField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; -pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { +pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs index cde3ab3bc0..7bc44e9ea4 100644 --- a/pairing/src/tests/repr.rs +++ b/pairing/src/tests/repr.rs @@ -1,10 +1,9 @@ -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; pub fn random_repr_tests() { random_encoding_tests::

(); - random_shl_tests::

(); random_shr_tests::

(); } @@ -15,71 +14,12 @@ fn random_encoding_tests() { ]); for _ in 0..1000 { - let r = P::random(&mut rng).into_repr(); + let r = P::random(&mut rng); - // Big endian - { - let mut rdecoded =

::Repr::default(); + let v = r.into_repr(); + let rdecoded = P::from_repr(v).unwrap(); - let mut v: Vec = vec![]; - r.write_be(&mut v).unwrap(); - rdecoded.read_be(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - // Little endian - { - let mut rdecoded =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_le(&mut v).unwrap(); - rdecoded.read_le(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - { - let mut rdecoded_le =

::Repr::default(); - let mut rdecoded_be_flip =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_le(&mut v).unwrap(); - - // This reads in little-endian, so we are done. - rdecoded_le.read_le(&v[..]).unwrap(); - - // This reads in big-endian, so we perform a swap of the - // bytes beforehand. - let v: Vec = v.into_iter().rev().collect(); - rdecoded_be_flip.read_be(&v[..]).unwrap(); - - assert_eq!(rdecoded_le, rdecoded_be_flip); - } - } -} - -fn random_shl_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); - - for shift in 0..=r.num_bits() { - let mut r1 = r; - let mut r2 = r; - - for _ in 0..shift { - r1.mul2(); - } - - r2.shl(shift); - - assert_eq!(r1, r2); - } + assert_eq!(r, rdecoded); } } @@ -90,19 +30,22 @@ fn random_shr_tests() { ]); for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); + let r = P::random(&mut rng); - for shift in 0..=r.num_bits() { - let mut r1 = r; - let mut r2 = r; + for shift in 0..P::NUM_BITS { + let r1 = r >> shift; + // Doubling the shifted element inserts zeros on the right; re-shifting should + // undo the doubling. + let mut r2 = r1; for _ in 0..shift { - r1.div2(); + r2 = r2.double(); } - - r2.shr(shift); + r2 = r2 >> shift; assert_eq!(r1, r2); } + + assert_eq!(r >> P::NUM_BITS, P::zero()); } } diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index 0ab1b6d51c..0872fbb8de 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -1,6 +1,6 @@ //! Generated code for handling light client protobuf structs. -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use zcash_primitives::{ block::{BlockHash, BlockHeader}, @@ -67,8 +67,8 @@ impl compact_formats::CompactOutput { /// [`CompactOutput.cmu`]: #structfield.cmu pub fn cmu(&self) -> Result { let mut repr = FrRepr::default(); - repr.read_le(&self.cmu[..]).map_err(|_| ())?; - Fr::from_repr(repr).map_err(|_| ()) + repr.as_mut().copy_from_slice(&self.cmu[..]); + Fr::from_repr(repr).ok_or(()) } /// Returns the ephemeral public key for this output. diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 14939d763d..d1f9bcd4db 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -183,7 +183,7 @@ pub fn scan_block( #[cfg(test)] mod tests { - use ff::{Field, PrimeField, PrimeFieldRepr}; + use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ @@ -207,9 +207,7 @@ mod tests { }; let fake_cmu = { let fake_cmu = Fr::random(rng); - let mut bytes = vec![]; - fake_cmu.into_repr().write_le(&mut bytes).unwrap(); - bytes + fake_cmu.into_repr().as_ref().to_owned() }; let fake_epk = { let mut buffer = vec![0; 64]; @@ -264,8 +262,7 @@ mod tests { Memo::default(), &mut rng, ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); + let cmu = note.cm(&JUBJUB).into_repr().as_ref().to_owned(); let mut epk = vec![]; encryptor.epk().write(&mut epk).unwrap(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index cbe4d0be0c..965b8d86de 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -1,4 +1,4 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{BitIterator, Field, PrimeField, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; @@ -83,15 +83,15 @@ impl PartialEq for Point { } impl Point { - pub fn read(reader: R, params: &E::Params) -> io::Result { + pub fn read(mut reader: R, params: &E::Params) -> io::Result { let mut y_repr = ::Repr::default(); - y_repr.read_le(reader)?; + reader.read_exact(y_repr.as_mut())?; - let x_sign = (y_repr.as_ref()[3] >> 63) == 1; - y_repr.as_mut()[3] &= 0x7fffffffffffffff; + let x_sign = (y_repr.as_ref()[31] >> 7) == 1; + y_repr.as_mut()[31] &= 0x7f; match E::Fr::from_repr(y_repr) { - Ok(y) => { + Some(y) => { let p = Self::get_for_y(y, x_sign, params); if bool::from(p.is_some()) { Ok(p.unwrap()) @@ -99,7 +99,7 @@ impl Point { Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) } } - Err(_) => Err(io::Error::new( + None => Err(io::Error::new( io::ErrorKind::InvalidInput, "y is not in field", )), @@ -167,17 +167,17 @@ impl Point { } impl Point { - pub fn write(&self, writer: W) -> io::Result<()> { + pub fn write(&self, mut writer: W) -> io::Result<()> { let (x, y) = self.to_xy(); assert_eq!(E::Fr::NUM_BITS, 255); let mut y_repr = y.into_repr(); if x.is_odd() { - y_repr.as_mut()[3] |= 0x8000000000000000u64; + y_repr.as_mut()[31] |= 0x80; } - y_repr.write_le(writer) + writer.write_all(y_repr.as_ref()) } /// Convert from a Montgomery point @@ -467,7 +467,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 85c3df4ce1..5e109d8d5c 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,7 +1,5 @@ -use ff::{ - adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField, PrimeFieldDecodingError, - PrimeFieldRepr, SqrtField, -}; +use byteorder::{ByteOrder, LittleEndian}; +use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField, SqrtField}; use rand_core::RngCore; use std::mem; use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; @@ -11,6 +9,11 @@ use super::ToUniform; // s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 const MODULUS: FsRepr = FsRepr([ + 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, + 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, +]); + +const MODULUS_LIMBS: Fs = Fs([ 0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, @@ -25,7 +28,7 @@ const MODULUS_BITS: u32 = 252; const REPR_SHAVE_BITS: u32 = 4; // R = 2**256 % s -const R: FsRepr = FsRepr([ +const R: Fs = Fs([ 0x25f80bb3b99607d9, 0xf315d62f66b6e750, 0x932514eeeb8814f4, @@ -33,7 +36,7 @@ const R: FsRepr = FsRepr([ ]); // R2 = R^2 % s -const R2: FsRepr = FsRepr([ +const R2: Fs = Fs([ 0x67719aa495e57731, 0x51b0cef09ce3fc26, 0x69dab7fac026e9a5, @@ -44,7 +47,7 @@ const R2: FsRepr = FsRepr([ const INV: u64 = 0x1ba3a358ef788ef9; // GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: FsRepr = FsRepr([ +const GENERATOR: Fs = Fs([ 0x720b1b19d49ea8f1, 0xbf4aa36101f13a58, 0x5fa8cc968193ccbb, @@ -55,7 +58,7 @@ const GENERATOR: FsRepr = FsRepr([ const S: u32 = 1; // 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: FsRepr = FsRepr([ +const ROOT_OF_UNITY: Fs = Fs([ 0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, @@ -63,199 +66,45 @@ const ROOT_OF_UNITY: FsRepr = FsRepr([ ]); // -((2**256) mod s) mod s -const NEGATIVE_ONE: Fs = Fs(FsRepr([ +const NEGATIVE_ONE: Fs = Fs([ 0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2, -])); +]); /// This is the underlying representation of an element of `Fs`. #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] -pub struct FsRepr(pub [u64; 4]); +pub struct FsRepr(pub [u8; 32]); impl ::std::fmt::Display for FsRepr { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "0x")?; for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; + write!(f, "{:02x}", *i)?; } Ok(()) } } -impl AsRef<[u64]> for FsRepr { +impl AsRef<[u8]> for FsRepr { #[inline(always)] - fn as_ref(&self) -> &[u64] { + fn as_ref(&self) -> &[u8] { &self.0 } } -impl AsMut<[u64]> for FsRepr { +impl AsMut<[u8]> for FsRepr { #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } -impl From for FsRepr { - #[inline(always)] - fn from(val: u64) -> FsRepr { - let mut repr = Self::default(); - repr.0[0] = val; - repr - } -} - -impl Ord for FsRepr { - #[inline(always)] - fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::std::cmp::Ordering::Less; - } else if a > b { - return ::std::cmp::Ordering::Greater; - } - } - - ::std::cmp::Ordering::Equal - } -} - -impl PartialOrd for FsRepr { - #[inline(always)] - fn partial_cmp(&self, other: &FsRepr) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl PrimeFieldRepr for FsRepr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (4 as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &FsRepr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &FsRepr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = sbb(*a, *b, &mut borrow); - } - } -} - /// This is an element of the scalar field of the Jubjub curve. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Fs(FsRepr); +pub struct Fs([u64; 4]); impl Default for Fs { fn default() -> Self { @@ -265,17 +114,23 @@ impl Default for Fs { impl ConstantTimeEq for Fs { fn ct_eq(&self, other: &Fs) -> Choice { - (self.0).0[0].ct_eq(&(other.0).0[0]) - & (self.0).0[1].ct_eq(&(other.0).0[1]) - & (self.0).0[2].ct_eq(&(other.0).0[2]) - & (self.0).0[3].ct_eq(&(other.0).0[3]) + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) } } impl Ord for Fs { #[inline(always)] fn cmp(&self, other: &Fs) -> ::std::cmp::Ordering { - self.into_repr().cmp(&other.into_repr()) + let mut a = *self; + a.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut b = *other; + b.mont_reduce(other.0[0], other.0[1], other.0[2], other.0[3], 0, 0, 0, 0); + + a.cmp_native(&b) } } @@ -297,7 +152,7 @@ impl From for Fs { fn from(val: u64) -> Fs { let mut raw = [0u64; 4]; raw[0] = val; - Fs(FsRepr(raw)) * Fs(R2) + Fs(raw) * R2 } } @@ -307,14 +162,20 @@ impl From for FsRepr { } } +impl<'a> From<&'a Fs> for FsRepr { + fn from(e: &'a Fs) -> FsRepr { + e.into_repr() + } +} + impl ConditionallySelectable for Fs { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fs(FsRepr([ - u64::conditional_select(&(a.0).0[0], &(b.0).0[0], choice), - u64::conditional_select(&(a.0).0[1], &(b.0).0[1], choice), - u64::conditional_select(&(a.0).0[2], &(b.0).0[2], choice), - u64::conditional_select(&(a.0).0[3], &(b.0).0[3], choice), - ])) + Fs([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) } } @@ -324,9 +185,9 @@ impl Neg for Fs { #[inline] fn neg(mut self) -> Self { if !self.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&self.0); - self.0 = tmp; + let mut tmp = MODULUS_LIMBS; + tmp.sub_noborrow(&self); + self = tmp; } self } @@ -356,7 +217,7 @@ impl<'r> AddAssign<&'r Fs> for Fs { #[inline] fn add_assign(&mut self, other: &Self) { // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); + self.add_nocarry(&other); // However, it may need to be reduced. self.reduce(); @@ -394,11 +255,11 @@ impl<'r> SubAssign<&'r Fs> for Fs { #[inline] fn sub_assign(&mut self, other: &Self) { // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); + if other.cmp_native(self) == ::core::cmp::Ordering::Greater { + self.add_nocarry(&MODULUS_LIMBS); } - self.0.sub_noborrow(&other.0); + self.sub_noborrow(&other); } } @@ -433,28 +294,28 @@ impl<'r> MulAssign<&'r Fs> for Fs { #[inline] fn mul_assign(&mut self, other: &Self) { let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); - let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r0 = mac_with_carry(0, self.0[0], other.0[0], &mut carry); + let r1 = mac_with_carry(0, self.0[0], other.0[1], &mut carry); + let r2 = mac_with_carry(0, self.0[0], other.0[2], &mut carry); + let r3 = mac_with_carry(0, self.0[0], other.0[3], &mut carry); let r4 = carry; let mut carry = 0; - let r1 = mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r1 = mac_with_carry(r1, self.0[1], other.0[0], &mut carry); + let r2 = mac_with_carry(r2, self.0[1], other.0[1], &mut carry); + let r3 = mac_with_carry(r3, self.0[1], other.0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[1], other.0[3], &mut carry); let r5 = carry; let mut carry = 0; - let r2 = mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r2 = mac_with_carry(r2, self.0[2], other.0[0], &mut carry); + let r3 = mac_with_carry(r3, self.0[2], other.0[1], &mut carry); + let r4 = mac_with_carry(r4, self.0[2], other.0[2], &mut carry); + let r5 = mac_with_carry(r5, self.0[2], other.0[3], &mut carry); let r6 = carry; let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r3 = mac_with_carry(r3, self.0[3], other.0[0], &mut carry); + let r4 = mac_with_carry(r4, self.0[3], other.0[1], &mut carry); + let r5 = mac_with_carry(r5, self.0[3], other.0[2], &mut carry); + let r6 = mac_with_carry(r6, self.0[3], other.0[3], &mut carry); let r7 = carry; self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); } @@ -472,17 +333,8 @@ impl BitAnd for Fs { #[inline(always)] fn bitand(mut self, rhs: u64) -> u64 { - self.mont_reduce( - (self.0).0[0], - (self.0).0[1], - (self.0).0[2], - (self.0).0[3], - 0, - 0, - 0, - 0, - ); - (self.0).0[0] & rhs + self.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + self.0[0] & rhs } } @@ -496,20 +348,11 @@ impl Shr for Fs { } // Convert from Montgomery to native representation. - self.mont_reduce( - (self.0).0[0], - (self.0).0[1], - (self.0).0[2], - (self.0).0[3], - 0, - 0, - 0, - 0, - ); + self.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); while n >= 64 { let mut t = 0; - for i in (self.0).0.iter_mut().rev() { + for i in self.0.iter_mut().rev() { mem::swap(&mut t, i); } n -= 64; @@ -517,7 +360,7 @@ impl Shr for Fs { if n > 0 { let mut t = 0; - for i in (self.0).0.iter_mut().rev() { + for i in self.0.iter_mut().rev() { let t2 = *i << (64 - n); *i >>= n; *i |= t; @@ -526,42 +369,42 @@ impl Shr for Fs { } // Convert back to Montgomery representation - self * Fs(R2) + self * R2 } } impl PrimeField for Fs { type Repr = FsRepr; - fn from_repr(r: FsRepr) -> Result { - let mut r = Fs(r); - if r.is_valid() { - r.mul_assign(&Fs(R2)); + fn from_repr(r: FsRepr) -> Option { + let r = { + let mut inner = [0; 4]; + LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); + Fs(inner) + }; - Ok(r) + if r.is_valid() { + Some(r * &R2) } else { - Err(PrimeFieldDecodingError::NotInField) + None } } fn into_repr(&self) -> FsRepr { let mut r = *self; - r.mont_reduce( - (self.0).0[0], - (self.0).0[1], - (self.0).0[2], - (self.0).0[3], - 0, - 0, - 0, - 0, - ); - r.0 + r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut repr = [0; 32]; + LittleEndian::write_u64_into(&r.0, &mut repr[..]); + FsRepr(repr) } #[inline(always)] fn is_odd(&self) -> bool { - self.into_repr().is_odd() + let mut r = *self; + r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + r.0[0] & 1 == 1 } fn char() -> FsRepr { @@ -573,13 +416,13 @@ impl PrimeField for Fs { const CAPACITY: u32 = Self::NUM_BITS - 1; fn multiplicative_generator() -> Self { - Fs(GENERATOR) + GENERATOR } const S: u32 = S; fn root_of_unity() -> Self { - Fs(ROOT_OF_UNITY) + ROOT_OF_UNITY } } @@ -591,7 +434,7 @@ impl Field for Fs { for limb in &mut repr { *limb = rng.next_u64(); } - Fs(FsRepr(repr)) + Fs(repr) }; // Mask away the unused most-significant bits. @@ -610,12 +453,12 @@ impl Field for Fs { #[inline] fn one() -> Self { - Fs(R) + R } #[inline] fn is_zero(&self) -> bool { - self.0.is_zero() + self.0.iter().all(|&e| e == 0) } #[inline] @@ -623,7 +466,13 @@ impl Field for Fs { let mut ret = *self; // This cannot exceed the backing capacity. - ret.0.mul2(); + let mut last = 0; + for i in &mut ret.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } // However, it may need to be reduced. ret.reduce(); @@ -658,16 +507,16 @@ impl Field for Fs { #[inline] fn square(&self) -> Self { let mut carry = 0; - let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r1 = mac_with_carry(0, self.0[0], self.0[1], &mut carry); + let r2 = mac_with_carry(0, self.0[0], self.0[2], &mut carry); + let r3 = mac_with_carry(0, self.0[0], self.0[3], &mut carry); let r4 = carry; let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r3 = mac_with_carry(r3, self.0[1], self.0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[1], self.0[3], &mut carry); let r5 = carry; let mut carry = 0; - let r5 = mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r5 = mac_with_carry(r5, self.0[2], self.0[3], &mut carry); let r6 = carry; let r7 = r6 >> 63; @@ -679,13 +528,13 @@ impl Field for Fs { let r1 = r1 << 1; let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r0 = mac_with_carry(0, self.0[0], self.0[0], &mut carry); let r1 = adc(r1, 0, &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r2 = mac_with_carry(r2, self.0[1], self.0[1], &mut carry); let r3 = adc(r3, 0, &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[2], self.0[2], &mut carry); let r5 = adc(r5, 0, &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r6 = mac_with_carry(r6, self.0[3], self.0[3], &mut carry); let r7 = adc(r7, 0, &mut carry); let mut ret = *self; @@ -695,11 +544,46 @@ impl Field for Fs { } impl Fs { + /// Compares two elements in native representation. This is only used + /// internally. + #[inline(always)] + fn cmp_native(&self, other: &Fs) -> ::std::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::std::cmp::Ordering::Less; + } else if a > b { + return ::std::cmp::Ordering::Greater; + } + } + + ::std::cmp::Ordering::Equal + } + /// Determines if the element is really in the field. This is only used /// internally. #[inline(always)] fn is_valid(&self) -> bool { - self.0 < MODULUS + // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use + // this internal function to eliminate the cycle. + self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &Fs) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &Fs) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = sbb(*a, *b, &mut borrow); + } } /// Subtracts the modulus from this element if this element is not in the @@ -707,7 +591,7 @@ impl Fs { #[inline(always)] fn reduce(&mut self) { if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); + self.sub_noborrow(&MODULUS_LIMBS); } } @@ -729,39 +613,39 @@ impl Fs { let k = r0.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r0, k, MODULUS.0[0], &mut carry); - r1 = mac_with_carry(r1, k, MODULUS.0[1], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[2], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + mac_with_carry(r0, k, MODULUS_LIMBS.0[0], &mut carry); + r1 = mac_with_carry(r1, k, MODULUS_LIMBS.0[1], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[2], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[3], &mut carry); r4 = adc(r4, 0, &mut carry); let carry2 = carry; let k = r1.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r1, k, MODULUS.0[0], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[1], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[2], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + mac_with_carry(r1, k, MODULUS_LIMBS.0[0], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[1], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[2], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[3], &mut carry); r5 = adc(r5, carry2, &mut carry); let carry2 = carry; let k = r2.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r2, k, MODULUS.0[0], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[1], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[2], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + mac_with_carry(r2, k, MODULUS_LIMBS.0[0], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[1], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[2], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[3], &mut carry); r6 = adc(r6, carry2, &mut carry); let carry2 = carry; let k = r3.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r3, k, MODULUS.0[0], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[1], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[2], &mut carry); - r6 = mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + mac_with_carry(r3, k, MODULUS_LIMBS.0[0], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[1], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[2], &mut carry); + r6 = mac_with_carry(r6, k, MODULUS_LIMBS.0[3], &mut carry); r7 = adc(r7, carry2, &mut carry); - (self.0).0[0] = r4; - (self.0).0[1] = r5; - (self.0).0[2] = r6; - (self.0).0[3] = r7; + self.0[0] = r4; + self.0[1] = r5; + self.0[2] = r6; + self.0[3] = r7; self.reduce(); } @@ -821,340 +705,26 @@ use rand_core::SeedableRng; #[cfg(test)] use rand_xorshift::XorShiftRng; -#[test] -fn test_fs_repr_ordering() { - fn assert_equality(a: FsRepr, b: FsRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); - } - - fn assert_lt(a: FsRepr, b: FsRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FsRepr([9999, 9999, 9999, 9999]), - FsRepr([9999, 9999, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9998, 9999, 9999]), - FsRepr([9999, 9998, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); - assert_lt( - FsRepr([9999, 9997, 9999, 9998]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9999, 9997, 9998, 9999]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); -} - -#[test] -fn test_fs_repr_from() { - assert_eq!(FsRepr::from(100), FsRepr([100, 0, 0, 0])); -} - -#[test] -fn test_fs_repr_is_odd() { - assert!(!FsRepr::from(0).is_odd()); - assert!(FsRepr::from(0).is_even()); - assert!(FsRepr::from(1).is_odd()); - assert!(!FsRepr::from(1).is_even()); - assert!(!FsRepr::from(324834872).is_odd()); - assert!(FsRepr::from(324834872).is_even()); - assert!(FsRepr::from(324834873).is_odd()); - assert!(!FsRepr::from(324834873).is_even()); -} - -#[test] -fn test_fs_repr_is_zero() { - assert!(FsRepr::from(0).is_zero()); - assert!(!FsRepr::from(1).is_zero()); - assert!(!FsRepr([0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fs_repr_div2() { - let mut a = FsRepr([ - 0xbd2920b19c972321, - 0x174ed0466a3be37e, - 0xd468d5e3b551f0b5, - 0xcb67c072733beefc, - ]); - a.div2(); - assert_eq!( - a, - FsRepr([ - 0x5e949058ce4b9190, - 0x8ba76823351df1bf, - 0x6a346af1daa8f85a, - 0x65b3e039399df77e - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FsRepr([ - 0x6fd7a524163392e4, - 0x16a2e9da08cd477c, - 0xdf9a8d1abc76aa3e, - 0x196cf80e4e677d - ]) - ); - for _ in 0..200 { - a.div2(); - } - assert_eq!(a, FsRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); - for _ in 0..40 { - a.div2(); - } - assert_eq!(a, FsRepr([0x19, 0x0, 0x0, 0x0])); - for _ in 0..4 { - a.div2(); - } - assert_eq!(a, FsRepr([0x1, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_shr() { - let mut a = FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, - ]); - a.shr(0); - assert_eq!( - a, - FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1 - ]) - ); - a.shr(1); - assert_eq!( - a, - FsRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0 - ]) - ); - a.shr(50); - assert_eq!( - a, - FsRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x75611bce1b4335e, - 0x6c0 - ]) - ); - a.shr(130); - assert_eq!(a, FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); - a.shr(64); - assert_eq!(a, FsRepr([0x1b0, 0x0, 0x0, 0x0])); -} - -#[test] -fn test_fs_repr_mul2() { - let mut a = FsRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FsRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); - for _ in 0..128 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x0, 0x9600000000000000])); - for _ in 0..7 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_num_bits() { - let mut a = FsRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FsRepr::from(1); - for i in 1..257 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fs_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ]); - t.sub_noborrow(&FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ])); - assert!( - t == FsRepr([ - 0xb813415048991c1f, - 0x10ad07ae88725d92, - 0x5a7b851271759961, - 0x36850eedd30c39c5 - ]) - ); - - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - a.0[3] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } -} - -#[test] -fn test_fr_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ]); - t.add_nocarry(&FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ])); - assert_eq!( - t, - FsRepr([ - 0x64b20e805c30a967, - 0x59a9ee9aa114a02, - 0x5871a104789020c9, - 0x8999707c5c726f65 - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - let mut b = Fs::random(&mut rng).into_repr(); - let mut c = Fs::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[3] >>= 3; - b.0[3] >>= 3; - c.0[3] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } -} - #[test] fn test_fs_is_valid() { - let mut a = Fs(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FsRepr::from(1)); + a.sub_noborrow(&Fs([1, 0, 0, 0])); assert!(a.is_valid()); - assert!(Fs(FsRepr::from(0)).is_valid()); - assert!(Fs(FsRepr([ + assert!(Fs::zero().is_valid()); + assert!(Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9 - ])) + ]) .is_valid()); - assert!(!Fs(FsRepr([ + assert!(!Fs([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -1178,77 +748,77 @@ fn test_fs_add_assign() { .unwrap(); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fs(FsRepr::from(0))); + tmp.add_assign(&Fs::zero()); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x8e6bfff4722d6e67, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162 - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fs(FsRepr::from(1))); + tmp.add_assign(&Fs([1, 0, 0, 0])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x8e6bfff4722d6e68, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162 - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fs(FsRepr([ + tmp.add_assign(&Fs([ 0xb634d07bc42d4a70, 0xf724f0c008411f5f, 0x456d4053d865af34, 0x24ce814e8c63027, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x44a0d070365ab8d8, 0x4d68cb1c91616459, 0xd9d3350659f7c99e, 0x4ac5d4227a3a189 - ])) + ]) ); // Add one to (s - 1) and test for the result. - tmp = Fs(FsRepr([ + tmp = Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9, - ])); - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fs([1, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is s - 1 - tmp = Fs(FsRepr([ + tmp = Fs([ 0xa11fda5950ce3636, 0x922e0dbccfe0ca0e, 0xacebb6e215b82d4a, 0x97ffb8cdc3aee93, - ])); - tmp.add_assign(&Fs(FsRepr([ + ]); + tmp.add_assign(&Fs([ 0x2f7734058628f680, 0x143a12d6fce74674, 0x597b841eeb7c0db6, 0x4fdb95d88f8c115, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9 - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fs([1, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -1282,71 +852,71 @@ fn test_fs_add_assign() { fn test_fs_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fs(FsRepr([ + let mut tmp = Fs([ 0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2, - ])); - tmp.sub_assign(&Fs(FsRepr([ + ]); + tmp.sub_assign(&Fs([ 0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x97c015841f9b79f6, 0xe7fcb121eb6ffc49, 0xb8c050814de2a3c1, 0x943c0589dcafa21 - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fs(FsRepr([ + tmp = Fs([ 0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679, - ])); - tmp.sub_assign(&Fs(FsRepr([ + ]); + tmp.sub_assign(&Fs([ 0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x38d6f8dab75bb2c1, 0xbe6b6f71e1581439, 0x4da6ea7fb351973e, 0x539f491c768b587 - ])) + ]) ); // Test for sensible results with zero - tmp = Fs(FsRepr::from(0)); - tmp.sub_assign(&Fs(FsRepr::from(0))); + tmp = Fs::zero(); + tmp.sub_assign(&Fs::from(0)); assert!(tmp.is_zero()); - tmp = Fs(FsRepr([ + tmp = Fs([ 0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230, - ])); - tmp.sub_assign(&Fs(FsRepr::from(0))); + ]); + tmp.sub_assign(&Fs::from(0)); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230 - ])) + ]) ); } @@ -1373,25 +943,25 @@ fn test_fs_sub_assign() { #[test] fn test_fs_mul_assign() { - let mut tmp = Fs(FsRepr([ + let mut tmp = Fs([ 0xb433b01287f71744, 0x4eafb86728c4d108, 0xfdd52c14b9dfbe65, 0x2ff1f3434821118, - ])); - tmp.mul_assign(&Fs(FsRepr([ + ]); + tmp.mul_assign(&Fs([ 0xdae00fc63c9fa90f, 0x5a5ed89b96ce21ce, 0x913cd26101bd6f58, 0x3f0822831697fe9, - ]))); + ])); assert!( - tmp == Fs(FsRepr([ + tmp == Fs([ 0xb68ecb61d54d2992, 0x5ff95874defce6a6, 0x3590eb053894657d, 0x53823a118515933 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -1443,80 +1013,73 @@ fn test_fs_mul_assign() { #[test] fn test_fs_shr() { let mut a = Fs::from_repr(FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x06003ab08de70da1, + 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, 0x7d, + 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, 0xb0, 0x3a, + 0x00, 0x06, ])) .unwrap(); a = a >> 0; assert_eq!( a.into_repr(), FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x06003ab08de70da1, + 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, + 0x7d, 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, + 0xb0, 0x3a, 0x00, 0x06, ]) ); a = a >> 1; assert_eq!( a.into_repr(), FsRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x03001d5846f386d0, + 0x1f, 0x14, 0x15, 0x24, 0x76, 0xdd, 0x9f, 0xd9, 0xef, 0x59, 0x46, 0xd4, 0x69, 0xf0, + 0xbe, 0xcc, 0x92, 0x2a, 0x07, 0x4d, 0x95, 0x16, 0x7b, 0xcd, 0xd0, 0x86, 0xf3, 0x46, + 0x58, 0x1d, 0x00, 0x03, ]) ); a = a >> 50; assert_eq!( a.into_repr(), FsRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x075611bce1b4335e, - 0x00000000000000c0, + 0x67, 0xf6, 0x7b, 0x96, 0x11, 0x75, 0x1a, 0xbc, 0x2f, 0xb3, 0xa4, 0xca, 0x41, 0x53, + 0xa5, 0xc5, 0x5e, 0x33, 0xb4, 0xe1, 0xbc, 0x11, 0x56, 0x07, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); a = a >> 130; assert_eq!( a.into_repr(), FsRepr([ - 0x01d5846f386d0cd7, - 0x0000000000000030, - 0x0000000000000000, - 0x0000000000000000, + 0xd7, 0x0c, 0x6d, 0x38, 0x6f, 0x84, 0xd5, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); a = a >> 64; assert_eq!( a.into_repr(), FsRepr([ - 0x0000000000000030, - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]) ); } #[test] -fn test_fr_squaring() { - let a = Fs(FsRepr([ +fn test_fs_squaring() { + let a = Fs([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xe7db4ea6533afa8, - ])); + ]); assert!(a.is_valid()); assert_eq!( a.square(), Fs::from_repr(FsRepr([ - 0x12c7f55cbc52fbaa, - 0xdedc98a0b5e6ce9e, - 0xad2892726a5396a, - 0x9fe82af8fee77b3 + 0xaa, 0xfb, 0x52, 0xbc, 0x5c, 0xf5, 0xc7, 0x12, 0x9e, 0xce, 0xe6, 0xb5, 0xa0, 0x98, + 0xdc, 0xde, 0x6a, 0x39, 0xa5, 0x26, 0x27, 0x89, 0xd2, 0x0a, 0xb3, 0x77, 0xee, 0x8f, + 0xaf, 0x82, 0xfe, 0x09, ])) .unwrap() ); @@ -1658,42 +1221,39 @@ fn test_fs_sqrt() { fn test_fs_from_into_repr() { // r + 1 should not be in the field assert!(Fs::from_repr(FsRepr([ - 0xd0970e5ed6f72cb8, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 + 0xb8, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, + 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, + 0x7d, 0x0e, ])) - .is_err()); + .is_none()); // r should not be in the field - assert!(Fs::from_repr(Fs::char()).is_err()); + assert!(Fs::from_repr(Fs::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. - let a = FsRepr([ - 0x5f2d0c05d0337b71, - 0xa1df2b0f8a20479, - 0xad73785e71bb863, - 0x504a00480c9acec, - ]); - let mut a_fs = Fs::from_repr(a).unwrap(); - let b = FsRepr([ - 0x66356ff51e477562, - 0x60a92ab55cf7603, - 0x8e4273c7364dd192, - 0x36df8844a344dc5, - ]); - let b_fs = Fs::from_repr(b).unwrap(); - let c = FsRepr([ - 0x7eef61708f4f2868, - 0x747a7e6cf52946fb, - 0x83dd75d7c9120017, - 0x762f5177f0f3df7, - ]); + let mut a_fs = Fs::from_repr(FsRepr([ + 0x71, 0x7b, 0x33, 0xd0, 0x05, 0x0c, 0x2d, 0x5f, 0x79, 0x04, 0xa2, 0xf8, 0xb0, 0xf2, 0x1d, + 0x0a, 0x63, 0xb8, 0x1b, 0xe7, 0x85, 0x37, 0xd7, 0x0a, 0xec, 0xac, 0xc9, 0x80, 0x04, 0xa0, + 0x04, 0x05, + ])) + .unwrap(); + let b_fs = Fs::from_repr(FsRepr([ + 0x62, 0x75, 0x47, 0x1e, 0xf5, 0x6f, 0x35, 0x66, 0x03, 0x76, 0xcf, 0x55, 0xab, 0x92, 0x0a, + 0x06, 0x92, 0xd1, 0x4d, 0x36, 0xc7, 0x73, 0x42, 0x8e, 0xc5, 0x4d, 0x34, 0x4a, 0x84, 0xf8, + 0x6d, 0x03, + ])) + .unwrap(); + let c_fs = Fs::from_repr(FsRepr([ + 0x68, 0x28, 0x4f, 0x8f, 0x70, 0x61, 0xef, 0x7e, 0xfb, 0x46, 0x29, 0xf5, 0x6c, 0x7e, 0x7a, + 0x74, 0x17, 0x00, 0x12, 0xc9, 0xd7, 0x75, 0xdd, 0x83, 0xf7, 0x3d, 0x0f, 0x7f, 0x17, 0xf5, + 0x62, 0x07, + ])) + .unwrap(); a_fs.mul_assign(&b_fs); - assert_eq!(a_fs.into_repr(), c); + assert_eq!(a_fs, c_fs); // Zero should be in the field. - assert!(Fs::from_repr(FsRepr::from(0)).unwrap().is_zero()); + assert!(Fs::from_repr(FsRepr::default()).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1712,60 +1272,15 @@ fn test_fs_from_into_repr() { } } -#[test] -fn test_fs_repr_display() { - assert_eq!( - format!( - "{}", - FsRepr([ - 0xa296db59787359df, - 0x8d3e33077430d318, - 0xd1abf5c606102eb7, - 0xcbc33ee28108f0 - ]) - ), - "0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0x14cb03535054a620, - 0x312aa2bf2d1dff52, - 0x970fe98746ab9361, - 0xc1e18acf82711e6 - ]) - ), - "0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FsRepr([0, 0, 0, 0])), - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - #[test] fn test_fs_display() { assert_eq!( format!( "{}", Fs::from_repr(FsRepr([ - 0x5528efb9998a01a3, - 0x5bd2add5cb357089, - 0xc061fa6adb491f98, - 0x70db9d143db03d9 + 0xa3, 0x01, 0x8a, 0x99, 0xb9, 0xef, 0x28, 0x55, 0x89, 0x70, 0x35, 0xcb, 0xd5, 0xad, + 0xd2, 0x5b, 0x98, 0x1f, 0x49, 0xdb, 0x6a, 0xfa, 0x61, 0xc0, 0xd9, 0x03, 0xdb, 0x43, + 0xd1, 0xb9, 0x0d, 0x07, ])) .unwrap() ), @@ -1775,10 +1290,9 @@ fn test_fs_display() { format!( "{}", Fs::from_repr(FsRepr([ - 0xd674745e2717999e, - 0xbeb1f52d3e96f338, - 0x9c7ae147549482b9, - 0x999706024530d22 + 0x9e, 0x99, 0x17, 0x27, 0x5e, 0x74, 0x74, 0xd6, 0x38, 0xf3, 0x96, 0x3e, 0x2d, 0xf5, + 0xb1, 0xbe, 0xb9, 0x82, 0x94, 0x54, 0x47, 0xe1, 0x7a, 0x9c, 0x22, 0x0d, 0x53, 0x24, + 0x60, 0x70, 0x99, 0x09, ])) .unwrap() ), diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index 0992637fe4..372f686eea 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -304,7 +304,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index 76914baaae..2f067a2e41 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -9,7 +9,7 @@ use crate::{ primitives::{ProofGenerationKey, ViewingKey}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use std::io::{self, Read, Write}; pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; @@ -71,14 +71,14 @@ impl ExpandedSpendingKey { pub fn read(mut reader: R) -> io::Result { let mut ask_repr = ::Repr::default(); - ask_repr.read_le(&mut reader)?; + reader.read_exact(ask_repr.as_mut())?; let ask = E::Fs::from_repr(ask_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?; let mut nsk_repr = ::Repr::default(); - nsk_repr.read_le(&mut reader)?; + reader.read_exact(nsk_repr.as_mut())?; let nsk = E::Fs::from_repr(nsk_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; let mut ovk = [0; 32]; reader.read_exact(&mut ovk)?; @@ -91,8 +91,8 @@ impl ExpandedSpendingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.ask.into_repr().write_le(&mut writer)?; - self.nsk.into_repr().write_le(&mut writer)?; + writer.write_all(self.ask.into_repr().as_ref())?; + writer.write_all(self.nsk.into_repr().as_ref())?; writer.write_all(&self.ovk.0)?; Ok(()) diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index 53600e5f3b..a3fc1fca72 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -511,9 +511,9 @@ mod tests { use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller}; use crate::sapling::Node; - use ff::PrimeFieldRepr; use hex; use pairing::bls12_381::FrRepr; + use std::convert::TryInto; use std::io::{self, Read, Write}; const HEX_EMPTY_ROOTS: [&str; 33] = [ @@ -1016,9 +1016,7 @@ mod tests { let mut paths_i = 0; let mut witness_ser_i = 0; for i in 0..16 { - let mut cm = FrRepr::default(); - cm.read_le(&hex::decode(commitments[i]).unwrap()[..]) - .expect("length is 32 bytes"); + let cm = FrRepr(hex::decode(commitments[i]).unwrap()[..].try_into().unwrap()); let cm = Node::new(cm); diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 747bd8d8ce..539ee643f3 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -11,9 +11,10 @@ use crate::{ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr}; use rand_core::{CryptoRng, RngCore}; +use std::convert::TryInto; use std::fmt; use std::str; @@ -192,7 +193,7 @@ fn prf_ock( let mut ock_input = [0u8; 128]; ock_input[0..32].copy_from_slice(&ovk.0); cv.write(&mut ock_input[32..64]).unwrap(); - cmu.into_repr().write_le(&mut ock_input[64..96]).unwrap(); + ock_input[64..96].copy_from_slice(cmu.into_repr().as_ref()); epk.write(&mut ock_input[96..128]).unwrap(); Blake2bParams::new() @@ -302,11 +303,7 @@ impl SaplingNoteEncryption { (&mut input[12..20]) .write_u64::(self.note.value) .unwrap(); - self.note - .r - .into_repr() - .write_le(&mut input[20..COMPACT_NOTE_SIZE]) - .unwrap(); + input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.into_repr().as_ref()); input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; @@ -330,10 +327,7 @@ impl SaplingNoteEncryption { let mut input = [0u8; OUT_PLAINTEXT_SIZE]; self.note.pk_d.write(&mut input[0..32]).unwrap(); - self.esk - .into_repr() - .write_le(&mut input[32..OUT_PLAINTEXT_SIZE]) - .unwrap(); + input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.into_repr().as_ref()); let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; assert_eq!( @@ -363,9 +357,11 @@ fn parse_note_plaintext_without_memo( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let rcm = Fs::from_repr(FsRepr( + plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let diversifier = Diversifier(d); let pk_d = diversifier @@ -483,9 +479,11 @@ pub fn try_sapling_output_recovery( .ok()? .as_prime_order(&JUBJUB)?; - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).ok()?; - let esk = Fs::from_repr(esk).ok()?; + let esk = Fs::from_repr(FsRepr( + op[32..OUT_PLAINTEXT_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let shared_secret = sapling_ka_agree(&esk, &pk_d); let key = kdf_sapling(shared_secret, &epk); @@ -515,9 +513,11 @@ pub fn try_sapling_output_recovery( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let rcm = Fs::from_repr(FsRepr( + plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -554,10 +554,11 @@ mod tests { primitives::{Diversifier, PaymentAddress, ValueCommitment}, }; use crypto_api_chachapoly::ChachaPolyIetf; - use ff::{Field, PrimeField, PrimeFieldRepr}; + use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use rand_core::OsRng; use rand_core::{CryptoRng, RngCore}; + use std::convert::TryInto; use std::str::FromStr; use super::{ @@ -791,9 +792,7 @@ mod tests { .as_prime_order(&JUBJUB) .unwrap(); - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).unwrap(); - let esk = Fs::from_repr(esk).unwrap(); + let esk = Fs::from_repr(FsRepr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())).unwrap(); let shared_secret = sapling_ka_agree(&esk, &pk_d); let key = kdf_sapling(shared_secret, &epk); @@ -1292,17 +1291,13 @@ mod tests { macro_rules! read_fr { ($field:expr) => {{ - let mut repr = FrRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fr::from_repr(repr).unwrap() + Fr::from_repr(FrRepr($field[..].try_into().unwrap())).unwrap() }}; } macro_rules! read_fs { ($field:expr) => {{ - let mut repr = FsRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fs::from_repr(repr).unwrap() + Fs::from_repr(FsRepr($field[..].try_into().unwrap())).unwrap() }}; } diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 6e01a1052c..cd06a60a9e 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -1,6 +1,6 @@ //! Structs for core Zcash primitives. -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use crate::constants; @@ -86,7 +86,7 @@ impl ViewingKey { h[31] &= 0b0000_0111; let mut e = ::Repr::default(); - e.read_le(&h[..]).unwrap(); + e.as_mut().copy_from_slice(&h[..]); E::Fs::from_repr(e).expect("should be a valid scalar") } diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index fcc3900f6c..c816ddfdd5 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -4,23 +4,23 @@ //! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown}; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use rand_core::RngCore; use std::io::{self, Read, Write}; use std::ops::{AddAssign, MulAssign, Neg}; use crate::util::hash_to_scalar; -fn read_scalar(reader: R) -> io::Result { +fn read_scalar(mut reader: R) -> io::Result { let mut s_repr = ::Repr::default(); - s_repr.read_le(reader)?; + reader.read_exact(s_repr.as_mut())?; E::Fs::from_repr(s_repr) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) } -fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { - s.into_repr().write_le(writer) +fn write_scalar(s: &E::Fs, mut writer: W) -> io::Result<()> { + writer.write_all(s.into_repr().as_ref()) } fn h_star(a: &[u8], b: &[u8]) -> E::Fs { diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index ecf5cd4007..4582fe663f 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -5,7 +5,7 @@ use crate::{ pedersen_hash::{pedersen_hash, Personalization}, primitives::Note, }; -use ff::{BitIterator, PrimeField, PrimeFieldRepr}; +use ff::{BitIterator, PrimeField}; use lazy_static::lazy_static; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use rand_core::{CryptoRng, RngCore}; @@ -21,7 +21,7 @@ pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let lhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { *a = b; } tmp @@ -29,7 +29,7 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let rhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(rhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(rhs)) { *a = b; } tmp @@ -62,13 +62,13 @@ impl Node { impl Hashable for Node { fn read(mut reader: R) -> io::Result { - let mut repr = FrRepr::default(); - repr.read_le(&mut reader)?; + let mut repr = FrRepr([0; 32]); + reader.read_exact(&mut repr.0)?; Ok(Node::new(repr)) } fn write(&self, mut writer: W) -> io::Result<()> { - self.repr.write_le(&mut writer) + writer.write_all(self.repr.as_ref()) } fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self { diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index dfc54fdc6a..d53ee7f154 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -2,7 +2,7 @@ use crate::jubjub::{edwards, Unknown}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use std::io::{self, Read, Write}; @@ -138,9 +138,10 @@ impl SpendDescription { // Consensus rule (§7.3): Canonical encoding is enforced here let anchor = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = FrRepr([0; 32]); + reader.read_exact(&mut f.0)?; + Fr::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))? }; let mut nullifier = [0; 32]; @@ -175,7 +176,7 @@ impl SpendDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - self.anchor.into_repr().write_le(&mut writer)?; + writer.write_all(self.anchor.into_repr().as_ref())?; writer.write_all(&self.nullifier)?; self.rk.write(&mut writer)?; writer.write_all(&self.zkproof)?; @@ -218,9 +219,10 @@ impl OutputDescription { // Consensus rule (§7.4): Canonical encoding is enforced here let cmu = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = FrRepr([0; 32]); + reader.read_exact(&mut f.0)?; + Fr::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))? }; // Consensus rules (§4.5): @@ -252,7 +254,7 @@ impl OutputDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - self.cmu.into_repr().write_le(&mut writer)?; + writer.write_all(self.cmu.into_repr().as_ref())?; self.ephemeral_key.write(&mut writer)?; writer.write_all(&self.enc_ciphertext)?; writer.write_all(&self.out_ciphertext)?; diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 4319f41752..c77c2d0a17 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -1,6 +1,6 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use super::{ components::{Amount, TxOut}, @@ -128,7 +128,7 @@ fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { s_spend.cv.write(&mut data).unwrap(); - s_spend.anchor.into_repr().write_le(&mut data).unwrap(); + data.extend_from_slice(s_spend.anchor.into_repr().as_ref()); data.extend_from_slice(&s_spend.nullifier); s_spend.rk.write(&mut data).unwrap(); data.extend_from_slice(&s_spend.zkproof); diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index e34767b46c..a02457f64c 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -453,7 +453,7 @@ impl ExtendedFullViewingKey { mod tests { use super::*; - use ff::{PrimeField, PrimeFieldRepr}; + use ff::PrimeField; #[test] fn derive_nonhardened_child() { @@ -1014,11 +1014,8 @@ mod tests { let xsk = &xsks[j]; let tv = &test_vectors[j]; - let mut buf = [0; 32]; - xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.ask.unwrap()); - xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.nsk.unwrap()); + assert_eq!(xsk.expsk.ask.into_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(xsk.expsk.nsk.into_repr().as_ref(), tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); @@ -1043,13 +1040,7 @@ mod tests { assert_eq!(xfvk.dk.0, tv.dk); assert_eq!(xfvk.chain_code.0, tv.c); - xfvk.fvk - .vk - .ivk() - .into_repr() - .write_le(&mut buf[..]) - .unwrap(); - assert_eq!(buf, tv.ivk); + assert_eq!(xfvk.fvk.vk.ivk().into_repr().as_ref(), tv.ivk); let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 01ed2d436b..59eb7614fe 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -769,7 +769,7 @@ mod test { let q = p.mul(s, params); let (x1, y1) = q.to_xy(); - let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); @@ -822,7 +822,7 @@ mod test { y: num_y0, }; - let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 7d1fbbabdc..5e6c05f1b6 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -615,8 +615,8 @@ fn test_input_circuit_with_bls12_381() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); lhs.reverse(); rhs.reverse(); @@ -799,8 +799,8 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); lhs.reverse(); rhs.reverse(); From 1761ebfb35548dbaea3dbfdc87ff024f557ea78e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 1 May 2020 13:48:30 +1200 Subject: [PATCH 013/210] ff: Remove SqrtField trait The sqrt() function is now part of the Field trait. ff_derive returns an error on fields for which it does not support generating a square root function. Note that Fq6 and Fq12 in pairing::bls12_381 leave the function unimplemented. They will be dropped once the migration to the bls12_381 crate is complete. The equivalent structs in that crate are not exposed. --- bellman/src/groth16/tests/dummy_engine.rs | 4 +- ff/ff_derive/src/lib.rs | 154 +++++++++++----------- ff/src/lib.rs | 13 +- group/src/lib.rs | 10 +- pairing/benches/bls12_381/fq.rs | 2 +- pairing/benches/bls12_381/fq2.rs | 2 +- pairing/benches/bls12_381/fr.rs | 2 +- pairing/src/bls12_381/ec.rs | 8 +- pairing/src/bls12_381/fq.rs | 4 - pairing/src/bls12_381/fq12.rs | 4 + pairing/src/bls12_381/fq2.rs | 4 +- pairing/src/bls12_381/fq6.rs | 4 + pairing/src/bls12_381/fr.rs | 4 - pairing/src/lib.rs | 6 +- pairing/src/tests/field.rs | 4 +- zcash_primitives/src/jubjub/edwards.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 40 +++--- zcash_primitives/src/jubjub/mod.rs | 4 +- zcash_primitives/src/jubjub/montgomery.rs | 2 +- zcash_primitives/src/jubjub/tests.rs | 2 +- 20 files changed, 131 insertions(+), 144 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 69937ce9c6..5c552e8a07 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,4 +1,4 @@ -use ff::{Field, PowVartime, PrimeField, ScalarEngine, SqrtField}; +use ff::{Field, PowVartime, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; @@ -217,9 +217,7 @@ impl Field for Fr { fn frobenius_map(&mut self, _: usize) { // identity } -} -impl SqrtField for Fr { fn sqrt(&self) -> CtOption { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 0a7db91a81..89b4afb465 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -163,8 +163,8 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { &modulus, &endianness, limbs, + sqrt_impl, )); - gen.extend(sqrt_impl); // Return the generated impl gen.into() @@ -486,89 +486,84 @@ fn prime_field_constants_and_sqrt( biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs); let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs); - let sqrt_impl = if (modulus % BigUint::from_str("4").unwrap()) - == BigUint::from_str("3").unwrap() - { - // Addition chain for (r + 1) // 4 - let mod_plus_1_over_4 = pow_fixed::generate( - "e! {self}, - (modulus + BigUint::from_str("1").unwrap()) >> 2, - ); - - quote! { - impl ::ff::SqrtField for #name { - fn sqrt(&self) -> ::subtle::CtOption { - use ::subtle::ConstantTimeEq; - - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = { - #mod_plus_1_over_4 - }; - - ::subtle::CtOption::new( - sqrt, - (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - } - } - } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - // Addition chain for (t - 1) // 2 - let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); + let sqrt_impl = + if (modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { + // Addition chain for (r + 1) // 4 + let mod_plus_1_over_4 = pow_fixed::generate( + "e! {self}, + (modulus + BigUint::from_str("1").unwrap()) >> 2, + ); - quote! { - impl ::ff::SqrtField for #name { - fn sqrt(&self) -> ::subtle::CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - use ::subtle::{ConditionallySelectable, ConstantTimeEq}; - - // w = self^((t - 1) // 2) - let w = { - #t_minus_1_over_2 - }; + quote! { + use ::subtle::ConstantTimeEq; - let mut v = S; - let mut x = *self * &w; - let mut b = x * &w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: ::subtle::Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&#name::one()); - let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = #name::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = #name::conditional_select(&z, &new_z, j_less_than_v); - } + // Because r = 3 (mod 4) + // sqrt can be done with only one exponentiation, + // via the computation of self^((r + 1) // 4) (mod r) + let sqrt = { + #mod_plus_1_over_4 + }; - let result = x * &z; - x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); - z = z.square(); - b *= &z; - v = k; + ::subtle::CtOption::new( + sqrt, + (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. + ) + } + } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { + // Addition chain for (t - 1) // 2 + let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); + + quote! { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + use ::subtle::{ConditionallySelectable, ConstantTimeEq}; + + // w = self^((t - 1) // 2) + let w = { + #t_minus_1_over_2 + }; + + let mut v = S; + let mut x = *self * &w; + let mut b = x * &w; + + // Initialize z as the 2^S root of unity. + let mut z = ROOT_OF_UNITY; + + for max_v in (1..=S).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: ::subtle::Choice = 1.into(); + + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&#name::one()); + let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = #name::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = #name::conditional_select(&z, &new_z, j_less_than_v); } - ::subtle::CtOption::new( - x, - (x * &x).ct_eq(self), // Only return Some if it's the square root. - ) + let result = x * &z; + x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); + z = z.square(); + b *= &z; + v = k; } + + ::subtle::CtOption::new( + x, + (x * &x).ct_eq(self), // Only return Some if it's the square root. + ) } - } - } else { - quote! {} - }; + } else { + syn::Error::new_spanned( + &name, + "ff_derive can't generate a square root function for this field.", + ) + .to_compile_error() + }; // Compute R^2 mod m let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); @@ -634,6 +629,7 @@ fn prime_field_impl( modulus: &BigUint, endianness: &ReprEndianness, limbs: usize, + sqrt_impl: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { // Returns r{n} as an ident. fn get_temp(n: usize) -> syn::Ident { @@ -1280,6 +1276,10 @@ fn prime_field_impl( { #squaring_impl } + + fn sqrt(&self) -> ::subtle::CtOption { + #sqrt_impl + } } impl #name { diff --git a/ff/src/lib.rs b/ff/src/lib.rs index bb2994cdef..e5b09f47c7 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -75,6 +75,10 @@ pub trait Field: /// Exponentiates this element by a power of the base prime modulus via /// the Frobenius automorphism. fn frobenius_map(&mut self, power: usize); + + /// Returns the square root of the field element, if it is + /// quadratic residue. + fn sqrt(&self) -> CtOption; } pub trait PowVartime: Field @@ -124,13 +128,6 @@ impl PowVartime for T { const LIMB_SIZE: u64 = 64; } -/// This trait represents an element of a field that has a square root operation described for it. -pub trait SqrtField: Field { - /// Returns the square root of the field element, if it is - /// quadratic residue. - fn sqrt(&self) -> CtOption; -} - /// This represents an element of a prime field. pub trait PrimeField: Field + Ord + From + BitAnd + Shr @@ -230,7 +227,7 @@ pub trait PrimeField: /// pairing-friendly curve) can be defined in a subtrait. pub trait ScalarEngine: Sized + 'static + Clone { /// This is the scalar field of the engine's groups. - type Fr: PrimeField + SqrtField; + type Fr: PrimeField; } #[derive(Debug)] diff --git a/group/src/lib.rs b/group/src/lib.rs index 89104945da..a330d1495a 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -1,7 +1,7 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use ff::{PrimeField, ScalarEngine, SqrtField}; +use ff::{Field, PrimeField, ScalarEngine}; use rand::RngCore; use std::error::Error; use std::fmt; @@ -47,8 +47,8 @@ pub trait CurveProjective: + CurveOpsOwned<::Affine> { type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; + type Scalar: PrimeField; + type Base: Field; type Affine: CurveAffine; /// Returns an element chosen uniformly at random using a user-provided RNG. @@ -105,8 +105,8 @@ pub trait CurveAffine: + Neg { type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; + type Scalar: PrimeField; + type Base: Field; type Projective: CurveProjective; type Uncompressed: EncodedPoint; type Compressed: EncodedPoint; diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs index 3c43fdd1f2..f2a981f875 100644 --- a/pairing/benches/bls12_381/fq.rs +++ b/pairing/benches/bls12_381/fq.rs @@ -3,7 +3,7 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use pairing::bls12_381::*; fn bench_fq_add_assign(c: &mut Criterion) { diff --git a/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs index 1eebb92db3..1402efa255 100644 --- a/pairing/benches/bls12_381/fq2.rs +++ b/pairing/benches/bls12_381/fq2.rs @@ -3,7 +3,7 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, MulAssign, SubAssign}; -use ff::{Field, SqrtField}; +use ff::Field; use pairing::bls12_381::*; fn bench_fq2_add_assign(c: &mut Criterion) { diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs index 33b3901e1b..f3aa749675 100644 --- a/pairing/benches/bls12_381/fr.rs +++ b/pairing/benches/bls12_381/fr.rs @@ -3,7 +3,7 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use pairing::bls12_381::*; fn bench_fr_add_assign(c: &mut Criterion) { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index ef03797783..d70d950f94 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -754,7 +754,7 @@ pub mod g1 { use super::super::{Bls12, Fq, Fq12, FqRepr, Fr}; use super::g2::G2Affine; use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, SqrtField}; + use ff::{BitIterator, Field, PrimeField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; @@ -1054,8 +1054,6 @@ pub mod g1 { #[test] fn g1_generator() { - use crate::SqrtField; - let mut x = Fq::zero(); let mut i = 0; loop { @@ -1366,7 +1364,7 @@ pub mod g2 { use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr}; use super::g1::G1Affine; use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, SqrtField}; + use ff::{BitIterator, Field, PrimeField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; @@ -1708,8 +1706,6 @@ pub mod g2 { #[test] fn g2_generator() { - use crate::SqrtField; - let mut x = Fq2::zero(); let mut i = 0; loop { diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index fa236ff9c5..0c2120eb0f 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1715,8 +1715,6 @@ fn test_fq_pow() { #[test] fn test_fq_sqrt() { - use ff::SqrtField; - let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -1846,8 +1844,6 @@ fn test_fq_num_bits() { #[test] fn test_fq_root_of_unity() { - use ff::SqrtField; - assert_eq!(Fq::S, 1); assert_eq!(Fq::multiplicative_generator(), Fq::from(2)); assert_eq!( diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs index f8b4853601..75b0860016 100644 --- a/pairing/src/bls12_381/fq12.rs +++ b/pairing/src/bls12_381/fq12.rs @@ -237,6 +237,10 @@ impl Field for Fq12 { c1: t.mul(&self.c1).neg(), }) } + + fn sqrt(&self) -> CtOption { + unimplemented!() + } } #[cfg(test)] diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index dd5b751f60..ce415abe7c 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -1,5 +1,5 @@ use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::{Field, PowVartime, SqrtField}; +use ff::{Field, PowVartime}; use rand_core::RngCore; use std::cmp::Ordering; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -241,9 +241,7 @@ impl Field for Fq2 { fn frobenius_map(&mut self, power: usize) { self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); } -} -impl SqrtField for Fq2 { /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! fn sqrt(&self) -> CtOption { diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs index b8ac627df7..2aa73ff770 100644 --- a/pairing/src/bls12_381/fq6.rs +++ b/pairing/src/bls12_381/fq6.rs @@ -391,6 +391,10 @@ impl Field for Fq6 { tmp }) } + + fn sqrt(&self) -> CtOption { + unimplemented!() + } } #[cfg(test)] diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 4a153ad32c..1ffc741bfb 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -495,8 +495,6 @@ fn test_fr_pow() { #[test] fn test_fr_sqrt() { - use ff::SqrtField; - let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -628,8 +626,6 @@ fn test_fr_num_bits() { #[test] fn test_fr_root_of_unity() { - use ff::SqrtField; - assert_eq!(Fr::S, 32); assert_eq!(Fr::multiplicative_generator(), Fr::from(7)); assert_eq!( diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 0081d811e6..645c70db3a 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -20,7 +20,7 @@ pub mod tests; pub mod bls12_381; -use ff::{Field, PrimeField, ScalarEngine, SqrtField}; +use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveOps, CurveOpsOwned, CurveProjective}; use subtle::CtOption; @@ -61,10 +61,10 @@ pub trait Engine: ScalarEngine { > + From; /// The base field that hosts G1. - type Fq: PrimeField + SqrtField; + type Fq: PrimeField; /// The extension field that hosts G2. - type Fqe: SqrtField; + type Fqe: Field; /// The extension field that hosts the target group of the pairing. type Fqk: Field; diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index 0288987ee6..0b924abd06 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -1,4 +1,4 @@ -use ff::{Field, PowVartime, PrimeField, SqrtField}; +use ff::{Field, PowVartime, PrimeField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -23,7 +23,7 @@ pub fn random_frobenius_tests>(characteristic: C, maxpo } } -pub fn random_sqrt_tests() { +pub fn random_sqrt_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index 965b8d86de..c4d6c804cd 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -1,4 +1,4 @@ -use ff::{BitIterator, Field, PrimeField, SqrtField}; +use ff::{BitIterator, Field, PrimeField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 5e109d8d5c..38771ba959 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,5 +1,5 @@ use byteorder::{ByteOrder, LittleEndian}; -use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField, SqrtField}; +use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField}; use rand_core::RngCore; use std::mem; use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; @@ -541,6 +541,24 @@ impl Field for Fs { ret.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); ret } + + fn sqrt(&self) -> CtOption { + // Shank's algorithm for s mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + // a1 = self^((s - 3) // 4) + let mut a1 = self.pow_vartime([ + 0xb425c397b5bdcb2du64, + 0x299a0824f3320420, + 0x4199cec0404d0ec0, + 0x39f6d3a994cebea, + ]); + let mut a0 = a1.square(); + a0.mul_assign(self); + a1.mul_assign(self); + + CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) + } } impl Fs { @@ -673,26 +691,6 @@ impl ToUniform for Fs { } } -impl SqrtField for Fs { - fn sqrt(&self) -> CtOption { - // Shank's algorithm for s mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) - - // a1 = self^((s - 3) // 4) - let mut a1 = self.pow_vartime([ - 0xb425c397b5bdcb2du64, - 0x299a0824f3320420, - 0x4199cec0404d0ec0, - 0x39f6d3a994cebea, - ]); - let mut a0 = a1.square(); - a0.mul_assign(self); - a1.mul_assign(self); - - CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) - } -} - #[test] fn test_neg_one() { let o = Fs::one().neg(); diff --git a/zcash_primitives/src/jubjub/mod.rs b/zcash_primitives/src/jubjub/mod.rs index 06a3810bf2..c94068161e 100644 --- a/zcash_primitives/src/jubjub/mod.rs +++ b/zcash_primitives/src/jubjub/mod.rs @@ -23,7 +23,7 @@ //! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub //! [BLS12-381]: pairing::bls12_381 -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use pairing::Engine; use crate::group_hash::group_hash; @@ -95,7 +95,7 @@ pub trait ToUniform { /// and some pre-computed parameters. pub trait JubjubEngine: Engine { /// The scalar field of the Jubjub curve - type Fs: PrimeField + SqrtField + ToUniform; + type Fs: PrimeField + ToUniform; /// The parameters of Jubjub and the Sapling protocol type Params: JubjubParams; } diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index 372f686eea..4b568021da 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -1,4 +1,4 @@ -use ff::{BitIterator, Field, PrimeField, SqrtField}; +use ff::{BitIterator, Field, PrimeField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index fca26b9160..595bf7c3e7 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -1,6 +1,6 @@ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use rand_core::{RngCore, SeedableRng}; From 55568b4d6ed248fc708f4632bac7dfd6bd52cf32 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 1 May 2020 14:01:43 +1200 Subject: [PATCH 014/210] ff: Remove frobenius_map from Field trait It is only used internally in the bls12_381 crate, and field extensions aren't exposed anywhere in the Zcash stack. --- bellman/src/groth16/tests/dummy_engine.rs | 4 ---- ff/ff_derive/src/lib.rs | 5 ----- ff/src/lib.rs | 4 ---- pairing/src/bls12_381/fq.rs | 1 - pairing/src/bls12_381/fq12.rs | 21 +++++++++------------ pairing/src/bls12_381/fq2.rs | 11 ++++------- pairing/src/bls12_381/fq6.rs | 21 +++++++++------------ pairing/src/bls12_381/fr.rs | 1 - pairing/src/tests/field.rs | 23 +---------------------- zcash_primitives/src/jubjub/fs.rs | 5 ----- 10 files changed, 23 insertions(+), 73 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5c552e8a07..712d44b35f 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -214,10 +214,6 @@ impl Field for Fr { } } - fn frobenius_map(&mut self, _: usize) { - // identity - } - fn sqrt(&self) -> CtOption { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 89b4afb465..410732cc83 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -1266,11 +1266,6 @@ fn prime_field_impl( #invert_impl } - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - #[inline] fn square(&self) -> Self { diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e5b09f47c7..4296cff099 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -72,10 +72,6 @@ pub trait Field: /// failing if the element is zero. fn invert(&self) -> CtOption; - /// Exponentiates this element by a power of the base prime modulus via - /// the Frobenius automorphism. - fn frobenius_map(&mut self, power: usize); - /// Returns the square root of the field element, if it is /// quadratic residue. fn sqrt(&self) -> CtOption; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 0c2120eb0f..4e1ee2cdba 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1865,7 +1865,6 @@ fn test_fq_root_of_unity() { fn fq_field_tests() { crate::tests::field::random_field_tests::(); crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(Fq::char(), 13); crate::tests::field::from_str_tests::(); } diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs index 75b0860016..3cab598cfd 100644 --- a/pairing/src/bls12_381/fq12.rs +++ b/pairing/src/bls12_381/fq12.rs @@ -39,6 +39,15 @@ impl Fq12 { self.c0.mul_by_nonresidue(); self.c0.add_assign(&aa); } + + pub fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } } impl ConditionallySelectable for Fq12 { @@ -200,15 +209,6 @@ impl Field for Fq12 { } } - fn frobenius_map(&mut self, power: usize) { - self.c0.frobenius_map(power); - self.c1.frobenius_map(power); - - self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - } - fn square(&self) -> Self { let mut ab = self.c0; ab.mul_assign(&self.c1); @@ -282,8 +282,5 @@ fn test_fq12_mul_by_014() { #[test] fn fq12_field_tests() { - use ff::PrimeField; - crate::tests::field::random_field_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index ce415abe7c..473753e405 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -53,6 +53,10 @@ impl Fq2 { t1 } + + pub fn frobenius_map(&mut self, power: usize) { + self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); + } } impl ConditionallySelectable for Fq2 { @@ -238,10 +242,6 @@ impl Field for Fq2 { }) } - fn frobenius_map(&mut self, power: usize) { - self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); - } - /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! fn sqrt(&self) -> CtOption { @@ -920,9 +920,6 @@ fn test_fq2_mul_nonresidue() { #[test] fn fq2_field_tests() { - use ff::PrimeField; - crate::tests::field::random_field_tests::(); crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); } diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs index 2aa73ff770..b0183df870 100644 --- a/pairing/src/bls12_381/fq6.rs +++ b/pairing/src/bls12_381/fq6.rs @@ -99,6 +99,15 @@ impl Fq6 { self.c1 = t2; self.c2 = t3; } + + pub fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + + self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); + } } impl ConditionallySelectable for Fq6 { @@ -305,15 +314,6 @@ impl Field for Fq6 { } } - fn frobenius_map(&mut self, power: usize) { - self.c0.frobenius_map(power); - self.c1.frobenius_map(power); - self.c2.frobenius_map(power); - - self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); - self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); - } - fn square(&self) -> Self { let s0 = self.c0.square(); let mut ab = self.c0; @@ -474,8 +474,5 @@ fn test_fq6_mul_by_01() { #[test] fn fq6_field_tests() { - use ff::PrimeField; - crate::tests::field::random_field_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); } diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 1ffc741bfb..886319ea72 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -645,7 +645,6 @@ fn test_fr_root_of_unity() { fn fr_field_tests() { crate::tests::field::random_field_tests::(); crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(Fr::char(), 13); crate::tests::field::from_str_tests::(); } diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index 0b924abd06..eb2c8fee62 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -1,28 +1,7 @@ -use ff::{Field, PowVartime, PrimeField}; +use ff::{Field, PrimeField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; -pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - for i in 0..=maxpower { - let mut a = F::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow_vartime(&characteristic); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - pub fn random_sqrt_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 38771ba959..731a3d11b7 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -499,11 +499,6 @@ impl Field for Fs { CtOption::new(inverse, Choice::from(if self.is_zero() { 0 } else { 1 })) } - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - #[inline] fn square(&self) -> Self { let mut carry = 0; From 38f87c2e7339269d5724307d83ac9b4a8586d358 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 1 May 2020 14:20:35 +1200 Subject: [PATCH 015/210] ff: Add PrimeField::ReprEndianness associated type This enables generic code to reliably operate on the bits of an encoded field element, by converting them to and from a known (little) endianness. The BitAnd and Shr bounds on PrimeField are now removed, as users can perform these operations themselves as needed. --- bellman/src/gadgets/test/mod.rs | 10 +-- bellman/src/groth16/tests/dummy_engine.rs | 1 + bellman/src/multiexp.rs | 16 +++- ff/Cargo.toml | 4 +- ff/ff_derive/src/lib.rs | 59 ++---------- ff/src/lib.rs | 38 ++++++-- pairing/src/bls12_381/fq.rs | 61 ------------- pairing/src/bls12_381/fr.rs | 55 ------------ pairing/src/tests/repr.rs | 28 ------ zcash_primitives/src/jubjub/fs.rs | 104 +--------------------- zcash_primitives/src/jubjub/tests.rs | 20 ++++- zcash_primitives/src/pedersen_hash.rs | 28 ++++-- 12 files changed, 101 insertions(+), 323 deletions(-) diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index b082907492..d09d87eea5 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,6 +1,6 @@ //! Helpers for testing circuit implementations. -use ff::{Field, PowVartime, PrimeField, ScalarEngine}; +use ff::{Endianness, Field, PowVartime, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; @@ -106,11 +106,9 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } } - // BLS12-381's Fr is canonically serialized in little-endian, but the hasher - // writes its coefficients in big endian. For now, we flip the endianness - // manually, which is not necessarily correct for circuits using other curves. - // TODO: Fix this in a standalone commit, and document the no-op change. - let coeff_be: Vec<_> = coeff.into_repr().as_ref().iter().cloned().rev().collect(); + let mut coeff_repr = coeff.into_repr(); + ::ReprEndianness::toggle_little_endian(&mut coeff_repr); + let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect(); buf[9..].copy_from_slice(&coeff_be[..]); h.update(&buf); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 712d44b35f..3cf426a41c 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -285,6 +285,7 @@ impl Default for FrRepr { impl PrimeField for Fr { type Repr = FrRepr; + type ReprEndianness = byteorder::LittleEndian; const NUM_BITS: u32 = 16; const CAPACITY: u32 = 15; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 18f48bd46f..f53ef9540e 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -1,6 +1,6 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Endianness, Field, PrimeField, ScalarEngine}; use futures::Future; use group::{CurveAffine, CurveProjective}; use std::io; @@ -195,8 +195,18 @@ where bases.skip(1)?; } } else { - let exp = exp >> skip; - let exp = exp & ((1 << c) - 1); + let mut exp = exp.into_repr(); + <::Fr as PrimeField>::ReprEndianness::toggle_little_endian(&mut exp); + + let exp = exp + .as_ref() + .into_iter() + .map(|b| (0..8).map(move |i| (b >> i) & 1u8)) + .flatten() + .skip(skip as usize) + .take(c as usize) + .enumerate() + .fold(0u64, |acc, (i, b)| acc + ((b as u64) << i)); if exp != 0 { (&mut buckets[(exp - 1) as usize]) diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 9dbf514ca3..01cc6c6513 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/ebfull/ff" edition = "2018" [dependencies] -byteorder = { version = "1", optional = true } +byteorder = { version = "1", default-features = false } ff_derive = { version = "0.6", path = "ff_derive", optional = true } rand_core = { version = "0.5", default-features = false } subtle = { version = "2.2.1", default-features = false, features = ["i128"] } @@ -19,7 +19,7 @@ subtle = { version = "2.2.1", default-features = false, features = ["i128"] } [features] default = ["std"] derive = ["ff_derive"] -std = ["byteorder"] +std = [] [badges] maintenance = { status = "actively-developed" } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 410732cc83..ad25c5ba1b 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -31,6 +31,13 @@ impl FromStr for ReprEndianness { } impl ReprEndianness { + fn repr_endianness(&self) -> proc_macro2::TokenStream { + match self { + ReprEndianness::Big => quote! {::byteorder::BigEndian}, + ReprEndianness::Little => quote! {::byteorder::LittleEndian}, + } + } + fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { let read_repr = match self { ReprEndianness::Big => quote! { @@ -885,6 +892,7 @@ fn prime_field_impl( let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); let mont_reduce_other_params = mont_reduce_params(quote! {other}, limbs); + let repr_endianness = endianness.repr_endianness(); let from_repr_impl = endianness.from_repr(name, limbs); let into_repr_impl = endianness.into_repr(repr, &mont_reduce_self_params, limbs); @@ -1117,58 +1125,9 @@ fn prime_field_impl( } } - impl ::core::ops::BitAnd for #name { - type Output = u64; - - #[inline(always)] - fn bitand(mut self, rhs: u64) -> u64 { - self.mont_reduce( - #mont_reduce_self_params - ); - - self.0[0] & rhs - } - } - - impl ::core::ops::Shr for #name { - type Output = #name; - - #[inline(always)] - fn shr(mut self, mut n: u32) -> #name { - if n as usize >= 64 * #limbs { - return Self::from(0); - } - - // Convert from Montgomery to native representation. - self.mont_reduce( - #mont_reduce_self_params - ); - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - - // Convert back to Montgomery representation - self * R2 - } - } - impl ::ff::PrimeField for #name { type Repr = #repr; + type ReprEndianness = #repr_endianness; fn from_repr(r: #repr) -> Option<#name> { #from_repr_impl diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 4296cff099..92a6079a84 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -12,6 +12,7 @@ extern crate std; #[cfg(feature = "derive")] pub use ff_derive::*; +use byteorder::ByteOrder; use core::convert::TryFrom; use core::fmt; use core::marker::PhantomData; @@ -124,14 +125,36 @@ impl PowVartime for T { const LIMB_SIZE: u64 = 64; } +/// Helper trait for converting the binary representation of a prime field element into a +/// specific endianness. This is useful when you need to act on the bit representation +/// of an element generically, as the native binary representation of a prime field is +/// field-dependent. +pub trait Endianness: ByteOrder { + /// Converts the provided representation between native and little-endian. + fn toggle_little_endian>(t: &mut T); +} + +impl Endianness for byteorder::BigEndian { + fn toggle_little_endian>(t: &mut T) { + t.as_mut().reverse(); + } +} + +impl Endianness for byteorder::LittleEndian { + fn toggle_little_endian>(_: &mut T) { + // No-op + } +} + /// This represents an element of a prime field. -pub trait PrimeField: - Field + Ord + From + BitAnd + Shr -{ +pub trait PrimeField: Field + Ord + From { /// The prime field can be converted back and forth into this binary /// representation. type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + for<'r> From<&'r Self>; + /// This indicates the endianness of [`PrimeField::Repr`]. + type ReprEndianness: Endianness; + /// Interpret a string of numbers as a (congruent) prime field element. /// Does not accept unnecessary leading zeroes or a blank string. fn from_str(s: &str) -> Option { @@ -176,16 +199,15 @@ pub trait PrimeField: /// this prime field, failing if the input is not canonical (is not smaller than the /// field's modulus). /// - /// The byte representation is interpreted with the same endianness as is returned - /// by [`PrimeField::into_repr`]. + /// The byte representation is interpreted with the endianness defined by + /// [`PrimeField::ReprEndianness`]. fn from_repr(_: Self::Repr) -> Option; /// Converts an element of the prime field into the standard byte representation for /// this field. /// - /// Endianness of the byte representation is defined by the field implementation. - /// Callers should assume that it is the standard endianness used to represent encoded - /// elements of this particular field. + /// The endianness of the byte representation is defined by + /// [`PrimeField::ReprEndianness`]. fn into_repr(&self) -> Self::Repr; /// Returns true iff this element is odd. diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 4e1ee2cdba..bc7abaf949 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1534,67 +1534,6 @@ fn test_fq_mul_assign() { } } -#[test] -fn test_fq_shr() { - let mut a = Fq::from_repr(FqRepr([ - 0x12, 0x25, 0xf2, 0x90, 0x1a, 0xea, 0x51, 0x4e, 0x16, 0x08, 0x0c, 0xf4, 0x07, 0x1e, 0x0b, - 0x05, 0xc5, 0x54, 0x1f, 0xd4, 0x80, 0x46, 0xb7, 0xe7, 0x9d, 0xdd, 0x5b, 0x31, 0x2f, 0x3d, - 0xd1, 0x04, 0x43, 0x24, 0x2c, 0x06, 0xae, 0xd5, 0x52, 0x87, 0xaa, 0x5c, 0xdd, 0x61, 0x72, - 0x84, 0x7f, 0xfd, - ])) - .unwrap(); - a = a >> 0; - assert_eq!( - a.into_repr(), - FqRepr([ - 0x12, 0x25, 0xf2, 0x90, 0x1a, 0xea, 0x51, 0x4e, 0x16, 0x08, 0x0c, 0xf4, 0x07, 0x1e, - 0x0b, 0x05, 0xc5, 0x54, 0x1f, 0xd4, 0x80, 0x46, 0xb7, 0xe7, 0x9d, 0xdd, 0x5b, 0x31, - 0x2f, 0x3d, 0xd1, 0x04, 0x43, 0x24, 0x2c, 0x06, 0xae, 0xd5, 0x52, 0x87, 0xaa, 0x5c, - 0xdd, 0x61, 0x72, 0x84, 0x7f, 0xfd, - ]) - ); - a = a >> 1; - assert_eq!( - a.into_repr(), - FqRepr([ - 0x09, 0x12, 0xf9, 0x48, 0x0d, 0x75, 0x28, 0xa7, 0x0b, 0x04, 0x06, 0x7a, 0x03, 0x8f, - 0x05, 0x82, 0xe2, 0xaa, 0x0f, 0xea, 0x40, 0x23, 0x5b, 0xf3, 0xce, 0xee, 0xad, 0x98, - 0x97, 0x9e, 0xe8, 0x82, 0x21, 0x92, 0x16, 0x03, 0x57, 0x6a, 0xa9, 0x43, 0xd5, 0x2e, - 0x6e, 0xb0, 0xb9, 0x42, 0x3f, 0xfe, - ]) - ); - a = a >> 50; - assert_eq!( - a.into_repr(), - FqRepr([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0xbe, 0x52, 0x03, 0x5d, 0x4a, 0x29, - 0xc2, 0xc1, 0x01, 0x9e, 0x80, 0xe3, 0xc1, 0x60, 0xb8, 0xaa, 0x83, 0xfa, 0x90, 0x08, - 0xd6, 0xfc, 0xf3, 0xbb, 0xab, 0x66, 0x25, 0xe7, 0xba, 0x20, 0x88, 0x64, 0x85, 0x80, - 0xd5, 0xda, 0xaa, 0x50, 0xf5, 0x4b, - ]) - ); - a = a >> 130; - assert_eq!( - a.into_repr(), - FqRepr([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x2f, 0x94, 0x80, 0xd7, - 0x52, 0x8a, 0x70, 0xb0, 0x40, 0x67, 0xa0, 0x38, 0xf0, 0x58, 0x2e, 0x2a, 0xa0, 0xfe, - 0xa4, 0x02, 0x35, 0xbf, 0x3c, 0xee, - ]) - ); - a = a >> 64; - assert_eq!( - a.into_repr(), - FqRepr([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x91, 0x2f, 0x94, 0x80, 0xd7, 0x52, 0x8a, 0x70, 0xb0, 0x40, 0x67, - 0xa0, 0x38, 0xf0, 0x58, 0x2e, 0x2a, - ]) - ); -} - #[test] fn test_fq_squaring() { let a = Fq([ diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 886319ea72..e01978d700 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -323,61 +323,6 @@ fn test_fr_mul_assign() { } } -#[test] -fn test_fr_shr() { - let mut a = Fr::from_repr(FrRepr([ - 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, 0x7d, - 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, 0xb0, 0x3a, - 0x00, 0x36, - ])) - .unwrap(); - a = a >> 0; - assert_eq!( - a.into_repr(), - FrRepr([ - 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, - 0x7d, 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, - 0xb0, 0x3a, 0x00, 0x36, - ]) - ); - a = a >> 1; - assert_eq!( - a.into_repr(), - FrRepr([ - 0x1f, 0x14, 0x15, 0x24, 0x76, 0xdd, 0x9f, 0xd9, 0xef, 0x59, 0x46, 0xd4, 0x69, 0xf0, - 0xbe, 0xcc, 0x92, 0x2a, 0x07, 0x4d, 0x95, 0x16, 0x7b, 0xcd, 0xd0, 0x86, 0xf3, 0x46, - 0x58, 0x1d, 0x00, 0x1b, - ]) - ); - a = a >> 50; - assert_eq!( - a.into_repr(), - FrRepr([ - 0x67, 0xf6, 0x7b, 0x96, 0x11, 0x75, 0x1a, 0xbc, 0x2f, 0xb3, 0xa4, 0xca, 0x41, 0x53, - 0xa5, 0xc5, 0x5e, 0x33, 0xb4, 0xe1, 0xbc, 0x11, 0x56, 0x07, 0xc0, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); - a = a >> 130; - assert_eq!( - a.into_repr(), - FrRepr([ - 0xd7, 0x0c, 0x6d, 0x38, 0x6f, 0x84, 0xd5, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); - a = a >> 64; - assert_eq!( - a.into_repr(), - FrRepr([ - 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); -} - #[test] fn test_fr_squaring() { let a = Fr([ diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs index 7bc44e9ea4..ca6112ed97 100644 --- a/pairing/src/tests/repr.rs +++ b/pairing/src/tests/repr.rs @@ -4,7 +4,6 @@ use rand_xorshift::XorShiftRng; pub fn random_repr_tests() { random_encoding_tests::

(); - random_shr_tests::

(); } fn random_encoding_tests() { @@ -22,30 +21,3 @@ fn random_encoding_tests() { assert_eq!(r, rdecoded); } } - -fn random_shr_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng); - - for shift in 0..P::NUM_BITS { - let r1 = r >> shift; - - // Doubling the shifted element inserts zeros on the right; re-shifting should - // undo the doubling. - let mut r2 = r1; - for _ in 0..shift { - r2 = r2.double(); - } - r2 = r2 >> shift; - - assert_eq!(r1, r2); - } - - assert_eq!(r >> P::NUM_BITS, P::zero()); - } -} diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 731a3d11b7..6a48f8ff43 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,8 +1,7 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField}; use rand_core::RngCore; -use std::mem; -use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::ToUniform; @@ -328,53 +327,9 @@ impl MulAssign for Fs { } } -impl BitAnd for Fs { - type Output = u64; - - #[inline(always)] - fn bitand(mut self, rhs: u64) -> u64 { - self.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - self.0[0] & rhs - } -} - -impl Shr for Fs { - type Output = Self; - - #[inline(always)] - fn shr(mut self, mut n: u32) -> Self { - if n as usize >= 64 * 4 { - return Self::from(0); - } - - // Convert from Montgomery to native representation. - self.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - - // Convert back to Montgomery representation - self * R2 - } -} - impl PrimeField for Fs { type Repr = FsRepr; + type ReprEndianness = byteorder::LittleEndian; fn from_repr(r: FsRepr) -> Option { let r = { @@ -1003,61 +958,6 @@ fn test_fs_mul_assign() { } } -#[test] -fn test_fs_shr() { - let mut a = Fs::from_repr(FsRepr([ - 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, 0x7d, - 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, 0xb0, 0x3a, - 0x00, 0x06, - ])) - .unwrap(); - a = a >> 0; - assert_eq!( - a.into_repr(), - FsRepr([ - 0x3f, 0x28, 0x2a, 0x48, 0xec, 0xba, 0x3f, 0xb3, 0xdf, 0xb3, 0x8c, 0xa8, 0xd3, 0xe0, - 0x7d, 0x99, 0x25, 0x55, 0x0e, 0x9a, 0x2a, 0x2d, 0xf6, 0x9a, 0xa1, 0x0d, 0xe7, 0x8d, - 0xb0, 0x3a, 0x00, 0x06, - ]) - ); - a = a >> 1; - assert_eq!( - a.into_repr(), - FsRepr([ - 0x1f, 0x14, 0x15, 0x24, 0x76, 0xdd, 0x9f, 0xd9, 0xef, 0x59, 0x46, 0xd4, 0x69, 0xf0, - 0xbe, 0xcc, 0x92, 0x2a, 0x07, 0x4d, 0x95, 0x16, 0x7b, 0xcd, 0xd0, 0x86, 0xf3, 0x46, - 0x58, 0x1d, 0x00, 0x03, - ]) - ); - a = a >> 50; - assert_eq!( - a.into_repr(), - FsRepr([ - 0x67, 0xf6, 0x7b, 0x96, 0x11, 0x75, 0x1a, 0xbc, 0x2f, 0xb3, 0xa4, 0xca, 0x41, 0x53, - 0xa5, 0xc5, 0x5e, 0x33, 0xb4, 0xe1, 0xbc, 0x11, 0x56, 0x07, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); - a = a >> 130; - assert_eq!( - a.into_repr(), - FsRepr([ - 0xd7, 0x0c, 0x6d, 0x38, 0x6f, 0x84, 0xd5, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); - a = a >> 64; - assert_eq!( - a.into_repr(), - FsRepr([ - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]) - ); -} - #[test] fn test_fs_squaring() { let a = Fs([ diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index 595bf7c3e7..a2f7fc7f73 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -1,6 +1,6 @@ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; -use ff::{Field, PrimeField}; +use ff::{Endianness, Field, PrimeField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use rand_core::{RngCore, SeedableRng}; @@ -372,7 +372,23 @@ fn test_jubjub_params(params: &E::Params) { let mut cur = E::Fs::one(); - let max = (-E::Fs::one()) >> 1; + let max = { + // Grab char - 1 in little endian. + let mut tmp = (-E::Fs::one()).into_repr(); + ::ReprEndianness::toggle_little_endian(&mut tmp); + + // Shift right by 1 bit. + let mut borrow = 0; + for b in tmp.as_mut().iter_mut().rev() { + let new_borrow = *b & 1; + *b = (borrow << 7) | (*b >> 1); + borrow = new_borrow; + } + + // Convert back to a field element. + ::ReprEndianness::toggle_little_endian(&mut tmp); + E::Fs::from_repr(tmp).unwrap() + }; let mut pacc = E::Fs::zero(); let mut nacc = E::Fs::zero(); diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index afd8a7325f..5d39cd6342 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -1,7 +1,8 @@ //! Implementation of the Pedersen hash function used in Sapling. use crate::jubjub::*; -use ff::Field; +use byteorder::{ByteOrder, LittleEndian}; +use ff::{Endianness, Field, PrimeField}; use std::ops::{AddAssign, Neg}; #[derive(Copy, Clone)] @@ -85,17 +86,32 @@ where let mut table: &[Vec>] = &generators.next().expect("we don't have enough generators"); - let window = JubjubBls12::pedersen_hash_exp_window_size(); - let window_mask = (1 << window) - 1; + let window = JubjubBls12::pedersen_hash_exp_window_size() as usize; + let window_mask = (1u64 << window) - 1; + + let mut acc = acc.into_repr(); + ::ReprEndianness::toggle_little_endian(&mut acc); + let num_limbs: usize = acc.as_ref().len() / 8; + let mut limbs = vec![0u64; num_limbs + 1]; + LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]); let mut tmp = edwards::Point::zero(); - while !acc.is_zero() { - let i = (acc & window_mask) as usize; + let mut pos = 0; + while pos < E::Fs::NUM_BITS as usize { + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let i = (if bit_idx + window < 64 { + // This window's bits are contained in a single u64. + limbs[u64_idx] >> bit_idx + } else { + // Combine the current u64's bits with the bits from the next u64. + (limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx)) + } & window_mask) as usize; tmp = tmp.add(&table[0][i], params); - acc = acc >> window; + pos += window; table = &table[1..]; } From fb31d09218b94bcfb807cc40ecf2a4b0b5ed775f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 15:48:51 +1200 Subject: [PATCH 016/210] ff: Remove Ord bound from PrimeField ff_derive still implements Ord and PartialOrd for the fields it implements, because pairing::bls12_381 internally assumes that those are implemented. Once we delete that implementation, we will remove the Ord and PartialOrd implementations from ff_derive. --- bellman/src/groth16/tests/dummy_engine.rs | 13 ------------- ff/src/lib.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 20 -------------------- zcash_primitives/src/jubjub/tests.rs | 23 ++++++++++++++++++----- 4 files changed, 19 insertions(+), 39 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 3cf426a41c..b738faf442 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -3,7 +3,6 @@ use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; -use std::cmp::Ordering; use std::fmt; use std::num::Wrapping; use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; @@ -48,18 +47,6 @@ impl ConditionallySelectable for Fr { } } -impl Ord for Fr { - fn cmp(&self, other: &Fr) -> Ordering { - (self.0).0.cmp(&(other.0).0) - } -} - -impl PartialOrd for Fr { - fn partial_cmp(&self, other: &Fr) -> Option { - Some(self.cmp(other)) - } -} - impl Neg for Fr { type Output = Self; diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 92a6079a84..d59ebfe9c4 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -147,7 +147,7 @@ impl Endianness for byteorder::LittleEndian { } /// This represents an element of a prime field. -pub trait PrimeField: Field + Ord + From { +pub trait PrimeField: Field + From { /// The prime field can be converted back and forth into this binary /// representation. type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + for<'r> From<&'r Self>; diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 6a48f8ff43..9f7c1b8214 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -120,26 +120,6 @@ impl ConstantTimeEq for Fs { } } -impl Ord for Fs { - #[inline(always)] - fn cmp(&self, other: &Fs) -> ::std::cmp::Ordering { - let mut a = *self; - a.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut b = *other; - b.mont_reduce(other.0[0], other.0[1], other.0[2], other.0[3], 0, 0, 0, 0); - - a.cmp_native(&b) - } -} - -impl PartialOrd for Fs { - #[inline(always)] - fn partial_cmp(&self, other: &Fs) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - impl ::std::fmt::Display for Fs { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fs({})", self.into_repr()) diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index a2f7fc7f73..6eeb1f5b03 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -385,9 +385,8 @@ fn test_jubjub_params(params: &E::Params) { borrow = new_borrow; } - // Convert back to a field element. - ::ReprEndianness::toggle_little_endian(&mut tmp); - E::Fs::from_repr(tmp).unwrap() + // Turns out we want this in little endian! + tmp }; let mut pacc = E::Fs::zero(); @@ -400,8 +399,22 @@ fn test_jubjub_params(params: &E::Params) { pacc += &tmp; nacc -= &tmp; // The first subtraction wraps intentionally. - assert!(pacc < max); - assert!(pacc < nacc); + let mut pacc_repr = pacc.into_repr(); + let mut nacc_repr = nacc.into_repr(); + ::ReprEndianness::toggle_little_endian(&mut pacc_repr); + ::ReprEndianness::toggle_little_endian(&mut nacc_repr); + + fn less_than(val: &[u8], bound: &[u8]) -> bool { + for (a, b) in val.iter().rev().zip(bound.iter().rev()) { + if a < b { + return true; + } + } + + false + } + assert!(less_than(pacc_repr.as_ref(), max.as_ref())); + assert!(less_than(pacc_repr.as_ref(), nacc_repr.as_ref())); // cur = cur * 16 for _ in 0..4 { From 15e229509a35ba468da7846ad29b6964a58cff89 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 17:57:19 +1200 Subject: [PATCH 017/210] ff: Move pow_vartime back into Field trait The only places we don't use constant u64 limbs, we use PrimeField::char instead (except in a single test where we use a field element). --- bellman/src/domain.rs | 2 +- bellman/src/gadgets/multieq.rs | 2 +- bellman/src/gadgets/test/mod.rs | 2 +- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 2 +- bellman/src/groth16/tests/mod.rs | 2 +- ff/src/lib.rs | 33 +++-------------------- pairing/src/bls12_381/fq.rs | 8 +++--- pairing/src/bls12_381/fq2.rs | 2 +- pairing/src/bls12_381/fr.rs | 8 +++--- pairing/src/bls12_381/mod.rs | 2 +- pairing/src/tests/engine.rs | 12 ++++++--- zcash_primitives/src/jubjub/fs.rs | 8 ++++-- 13 files changed, 36 insertions(+), 49 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index f1c25922fd..0e9192e446 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -11,7 +11,7 @@ //! [`EvaluationDomain`]: crate::domain::EvaluationDomain //! [Groth16]: https://eprint.iacr.org/2016/260 -use ff::{Field, PowVartime, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField, ScalarEngine}; use group::CurveProjective; use std::ops::{AddAssign, MulAssign, SubAssign}; diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs index 890eb7c6e7..37b2d9429e 100644 --- a/bellman/src/gadgets/multieq.rs +++ b/bellman/src/gadgets/multieq.rs @@ -1,4 +1,4 @@ -use ff::{PowVartime, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index d09d87eea5..25f0c8597b 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,6 +1,6 @@ //! Helpers for testing circuit implementations. -use ff::{Endianness, Field, PowVartime, PrimeField, ScalarEngine}; +use ff::{Endianness, Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 1d86992299..02efc21bd6 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -2,7 +2,7 @@ use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::{Field, PowVartime}; +use ff::Field; use group::{CurveAffine, CurveProjective, Wnaf}; use pairing::Engine; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index b738faf442..14bd58865f 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,4 +1,4 @@ -use ff::{Field, PowVartime, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index 2914bf2c04..276738c85d 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -1,4 +1,4 @@ -use ff::{Field, PowVartime, PrimeField}; +use ff::{Field, PrimeField}; use pairing::Engine; mod dummy_engine; diff --git a/ff/src/lib.rs b/ff/src/lib.rs index d59ebfe9c4..2175de09af 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -76,36 +76,21 @@ pub trait Field: /// Returns the square root of the field element, if it is /// quadratic residue. fn sqrt(&self) -> CtOption; -} - -pub trait PowVartime: Field -where - L: Copy + PartialEq + PartialOrd + AddAssign, - L: BitAnd, - L: Shr, - L: Sub, -{ - const ZERO: L; - const ONE: L; - const LIMB_SIZE: L; /// Exponentiates `self` by `exp`, where `exp` is a little-endian order /// integer exponent. /// /// **This operation is variable time with respect to the exponent.** If the /// exponent is fixed, this operation is effectively constant time. - fn pow_vartime>(&self, exp: S) -> Self { + fn pow_vartime>(&self, exp: S) -> Self { let mut res = Self::one(); for e in exp.as_ref().iter().rev() { - let mut i = Self::ZERO; - while i < Self::LIMB_SIZE { + for i in (0..64).rev() { res = res.square(); - if ((*e >> (Self::LIMB_SIZE - Self::ONE - i)) & Self::ONE) == Self::ONE { + if ((*e >> i) & 1) == 1 { res.mul_assign(self); } - - i += Self::ONE; } } @@ -113,18 +98,6 @@ where } } -impl PowVartime for T { - const ZERO: u8 = 0; - const ONE: u8 = 1; - const LIMB_SIZE: u8 = 8; -} - -impl PowVartime for T { - const ZERO: u64 = 0; - const ONE: u64 = 1; - const LIMB_SIZE: u64 = 64; -} - /// Helper trait for converting the binary representation of a prime field element into a /// specific endianness. This is useful when you need to act on the bit representation /// of an element generically, as the native binary representation of a prime field is diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index bc7abaf949..4daf4bd616 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -2,8 +2,6 @@ use super::fq2::Fq2; use ff::{Field, PrimeField}; use std::ops::{AddAssign, MulAssign, SubAssign}; -#[cfg(test)] -use ff::PowVartime; #[cfg(test)] use std::ops::Neg; @@ -1644,11 +1642,15 @@ fn test_fq_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 6]; + byteorder::LittleEndian::read_u64_into(Fq::char().as_ref(), &mut char_limbs); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fq::random(&mut rng); - assert_eq!(a, a.pow_vartime(Fq::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 473753e405..38b48bce46 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -1,5 +1,5 @@ use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::{Field, PowVartime}; +use ff::Field; use rand_core::RngCore; use std::cmp::Ordering; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index e01978d700..3e2e1f69e7 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -7,8 +7,6 @@ use std::ops::{AddAssign, MulAssign, SubAssign}; #[PrimeFieldReprEndianness = "little"] pub struct Fr([u64; 4]); -#[cfg(test)] -use ff::PowVartime; #[cfg(test)] use rand_core::SeedableRng; #[cfg(test)] @@ -430,11 +428,15 @@ fn test_fr_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(Fr::char().as_ref(), &mut char_limbs); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fr::random(&mut rng); - assert_eq!(a, a.pow_vartime(Fr::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 0f18053215..afe9c826a1 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -23,7 +23,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, PairingCurveAffine}; -use ff::{BitIterator, Field, PowVartime, ScalarEngine}; +use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index c65816de1a..d4efb6dc77 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,10 +1,10 @@ -use ff::PowVartime; +use ff::{Endianness, Field, PrimeField}; use group::{CurveAffine, CurveProjective}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; -use crate::{Engine, Field, PairingCurveAffine, PrimeField}; +use crate::{Engine, PairingCurveAffine}; pub fn engine_tests() { let mut rng = XorShiftRng::from_seed([ @@ -130,8 +130,14 @@ fn random_bilinearity_tests() { let mut cd = c; cd.mul_assign(&d); + let mut cd = cd.into_repr(); + ::ReprEndianness::toggle_little_endian(&mut cd); - let abcd = E::pairing(a, b).pow_vartime(cd.into_repr()); + use byteorder::ByteOrder; + let mut cd_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(cd.as_ref(), &mut cd_limbs); + + let abcd = E::pairing(a, b).pow_vartime(cd_limbs); assert_eq!(acbd, adbc); assert_eq!(acbd, abcd); diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 9f7c1b8214..816c896e77 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,5 +1,5 @@ use byteorder::{ByteOrder, LittleEndian}; -use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PowVartime, PrimeField}; +use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PrimeField}; use rand_core::RngCore; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -1051,11 +1051,15 @@ fn test_fs_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(Fs::char().as_ref(), &mut char_limbs); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fs::random(&mut rng); - assert_eq!(a, a.pow_vartime(Fs::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } From 9114c367f4a2d5ddd5fa0df8ffdd036669c26474 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 18:25:26 +1200 Subject: [PATCH 018/210] ff_derive: Generate modulus representation with correct endianness Now that PrimeField::ReprEndianness exists, users can obtain a known-endianness representation from the output of PrimeField::char (which is a PrimeField::Repr, and should return a representation with the same endianness as PrimeField::into_repr). --- ff/ff_derive/src/lib.rs | 34 ++++++++++++++++++++++++------ pairing/src/bls12_381/fq.rs | 3 ++- pairing/src/bls12_381/tests/mod.rs | 34 ++++++++++++------------------ 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index ad25c5ba1b..9fcf5a7ca8 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -38,6 +38,23 @@ impl ReprEndianness { } } + fn modulus_repr(&self, modulus: &BigUint, bytes: usize) -> Vec { + match self { + ReprEndianness::Big => { + let buf = modulus.to_bytes_be(); + iter::repeat(0) + .take(bytes - buf.len()) + .chain(buf.into_iter()) + .collect() + } + ReprEndianness::Little => { + let mut buf = modulus.to_bytes_le(); + buf.extend(iter::repeat(0).take(bytes - buf.len())); + buf + } + } + } + fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { let read_repr = match self { ReprEndianness::Big => quote! { @@ -159,8 +176,14 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut gen = proc_macro2::TokenStream::new(); - let (constants_impl, sqrt_impl) = - prime_field_constants_and_sqrt(&ast.ident, &repr_ident, &modulus, limbs, generator); + let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( + &ast.ident, + &repr_ident, + &modulus, + &endianness, + limbs, + generator, + ); gen.extend(constants_impl); gen.extend(prime_field_repr_impl(&repr_ident, &endianness, limbs * 8)); @@ -466,6 +489,7 @@ fn prime_field_constants_and_sqrt( name: &syn::Ident, repr: &syn::Ident, modulus: &BigUint, + endianness: &ReprEndianness, limbs: usize, generator: BigUint, ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { @@ -576,11 +600,7 @@ fn prime_field_constants_and_sqrt( let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); let r = biguint_to_u64_vec(r, limbs); - let modulus_repr = { - let mut buf = modulus.to_bytes_le(); - buf.extend(iter::repeat(0).take((limbs * 8) - buf.len())); - buf - }; + let modulus_repr = endianness.modulus_repr(modulus, limbs * 8); let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 4daf4bd616..a84046712b 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1644,7 +1644,8 @@ fn test_fq_pow() { use byteorder::ByteOrder; let mut char_limbs = [0; 6]; - byteorder::LittleEndian::read_u64_into(Fq::char().as_ref(), &mut char_limbs); + byteorder::BigEndian::read_u64_into(Fq::char().as_ref(), &mut char_limbs); + char_limbs.reverse(); for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 6d9252e83a..6d8cc927f7 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -147,13 +147,11 @@ fn test_g1_uncompressed_invalid_vectors() { } } - // PrimeField::char() returns the modulus in its little-endian byte representation, - // but Fq field elements use big-endian encoding, so flip the endianness. - let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); + let m = Fq::char(); { let mut o = o; - o.as_mut()[..48].copy_from_slice(&m[..]); + o.as_mut()[..48].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate"); @@ -164,7 +162,7 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut o = o; - o.as_mut()[48..].copy_from_slice(&m[..]); + o.as_mut()[48..].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate"); @@ -265,13 +263,11 @@ fn test_g2_uncompressed_invalid_vectors() { } } - // PrimeField::char() returns the modulus in its little-endian byte representation, - // but Fq field elements use big-endian encoding, so flip the endianness. - let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); + let m = Fq::char(); { let mut o = o; - o.as_mut()[..48].copy_from_slice(&m[..]); + o.as_mut()[..48].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c1)"); @@ -282,7 +278,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - o.as_mut()[48..96].copy_from_slice(&m[..]); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c0)"); @@ -293,7 +289,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - o.as_mut()[96..144].copy_from_slice(&m[..]); + o.as_mut()[96..144].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c1)"); @@ -304,7 +300,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - o.as_mut()[144..].copy_from_slice(&m[..]); + o.as_mut()[144..].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c0)"); @@ -411,13 +407,11 @@ fn test_g1_compressed_invalid_vectors() { } } - // PrimeField::char() returns the modulus in its little-endian byte representation, - // but Fq field elements use big-endian encoding, so flip the endianness. - let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); + let m = Fq::char(); { let mut o = o; - o.as_mut()[..48].copy_from_slice(&m[..]); + o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { @@ -527,13 +521,11 @@ fn test_g2_compressed_invalid_vectors() { } } - // PrimeField::char() returns the modulus in its little-endian byte representation, - // but Fq field elements use big-endian encoding, so flip the endianness. - let m: Vec<_> = Fq::char().as_ref().iter().cloned().rev().collect(); + let m = Fq::char(); { let mut o = o; - o.as_mut()[..48].copy_from_slice(&m[..]); + o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { @@ -545,7 +537,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut o = o; - o.as_mut()[48..96].copy_from_slice(&m[..]); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { From c597db59a61951723095ea6cad51d35b0a53814b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 18:55:13 +1200 Subject: [PATCH 019/210] ff: Rename PrimeField::into_repr -> PrimeField::to_repr --- bellman/src/domain.rs | 2 +- bellman/src/gadgets/boolean.rs | 2 +- bellman/src/gadgets/num.rs | 6 ++-- bellman/src/gadgets/test/mod.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 2 +- bellman/src/groth16/verifier.rs | 2 +- bellman/src/multiexp.rs | 4 +-- ff/ff_derive/src/lib.rs | 18 ++++++------ ff/src/lib.rs | 2 +- group/src/tests/mod.rs | 2 +- group/src/wnaf.rs | 4 +-- pairing/benches/bls12_381/fq.rs | 10 +++---- pairing/benches/bls12_381/fr.rs | 10 +++---- pairing/src/bls12_381/ec.rs | 18 ++++++------ pairing/src/bls12_381/fq.rs | 6 ++-- pairing/src/bls12_381/fr.rs | 6 ++-- pairing/src/bls12_381/tests/mod.rs | 28 +++++++++---------- pairing/src/tests/engine.rs | 2 +- pairing/src/tests/repr.rs | 2 +- zcash_client_backend/src/welding_rig.rs | 6 ++-- zcash_primitives/src/jubjub/edwards.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 12 ++++---- zcash_primitives/src/jubjub/tests.rs | 6 ++-- zcash_primitives/src/keys.rs | 4 +-- zcash_primitives/src/merkle_tree.rs | 6 ++-- zcash_primitives/src/note_encryption.rs | 10 +++---- zcash_primitives/src/pedersen_hash.rs | 2 +- zcash_primitives/src/redjubjub.rs | 2 +- zcash_primitives/src/sapling.rs | 4 +-- zcash_primitives/src/transaction/builder.rs | 6 ++-- .../src/transaction/components.rs | 4 +-- zcash_primitives/src/transaction/sighash.rs | 2 +- zcash_primitives/src/zip32.rs | 6 ++-- zcash_proofs/src/circuit/ecc.rs | 4 +-- zcash_proofs/src/circuit/sapling.rs | 8 +++--- 35 files changed, 106 insertions(+), 106 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 0e9192e446..be97c2037b 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -221,7 +221,7 @@ impl Group for Point { Point(G::zero()) } fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by.into_repr()); + self.0.mul_assign(by.to_repr()); } fn group_add_assign(&mut self, other: &Self) { self.0.add_assign(&other.0); diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index 2ccad51a7f..b521e7bdfe 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -318,7 +318,7 @@ pub fn field_into_allocated_bits_le, F: let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); let mut found_one = false; - for b in BitIterator::::new(value.into_repr()) { + for b in BitIterator::::new(value.to_repr()) { // Skip leading bits found_one |= field_char.next().unwrap(); if !found_one { diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index 8f736636d4..236689d2b7 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -103,8 +103,8 @@ impl AllocatedNum { // We want to ensure that the bit representation of a is // less than or equal to r - 1. - let mut a = self.value.map(|e| BitIterator::::new(e.into_repr())); - let b = (-E::Fr::one()).into_repr(); + let mut a = self.value.map(|e| BitIterator::::new(e.to_repr())); + let b = (-E::Fr::one()).to_repr(); let mut result = vec![]; @@ -557,7 +557,7 @@ mod test { assert!(cs.is_satisfied()); - for (b, a) in BitIterator::::new(r.into_repr()) + for (b, a) in BitIterator::::new(r.to_repr()) .skip(1) .zip(bits.iter().rev()) { diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index 25f0c8597b..be7214ea2a 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -106,7 +106,7 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } } - let mut coeff_repr = coeff.into_repr(); + let mut coeff_repr = coeff.to_repr(); ::ReprEndianness::toggle_little_endian(&mut coeff_repr); let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect(); buf[9..].copy_from_slice(&coeff_be[..]); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 14bd58865f..fccf5b00b5 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -287,7 +287,7 @@ impl PrimeField for Fr { } } - fn into_repr(&self) -> FrRepr { + fn to_repr(&self) -> FrRepr { FrRepr::from(*self) } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 5983667158..0c8910151e 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -31,7 +31,7 @@ pub fn verify_proof<'a, E: Engine>( let mut acc = pvk.ic[0].into_projective(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { - AddAssign::<&E::G1>::add_assign(&mut acc, &b.mul(i.into_repr())); + AddAssign::<&E::G1>::add_assign(&mut acc, &b.mul(i.to_repr())); } // The original verification equation is: diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index f53ef9540e..deed9fa05f 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -195,7 +195,7 @@ where bases.skip(1)?; } } else { - let mut exp = exp.into_repr(); + let mut exp = exp.to_repr(); <::Fr as PrimeField>::ReprEndianness::toggle_little_endian(&mut exp); let exp = exp @@ -305,7 +305,7 @@ fn test_with_bls12() { let mut acc = G::zero(); for (base, exp) in bases.iter().zip(exponents.iter()) { - AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.into_repr())); + AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.to_repr())); } acc diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 9fcf5a7ca8..f04ecfa13a 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -83,7 +83,7 @@ impl ReprEndianness { } } - fn into_repr( + fn to_repr( &self, repr: &syn::Ident, mont_reduce_self_params: &proc_macro2::TokenStream, @@ -914,7 +914,7 @@ fn prime_field_impl( let repr_endianness = endianness.repr_endianness(); let from_repr_impl = endianness.from_repr(name, limbs); - let into_repr_impl = endianness.into_repr(repr, &mont_reduce_self_params, limbs); + let to_repr_impl = endianness.to_repr(repr, &mont_reduce_self_params, limbs); let top_limb_index = limbs - 1; @@ -935,7 +935,7 @@ fn prime_field_impl( impl ::subtle::ConstantTimeEq for #name { fn ct_eq(&self, other: &#name) -> ::subtle::Choice { - self.into_repr().ct_eq(&other.into_repr()) + self.to_repr().ct_eq(&other.to_repr()) } } @@ -951,7 +951,7 @@ fn prime_field_impl( impl ::core::fmt::Debug for #name { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:?})", stringify!(#name), self.into_repr()) + write!(f, "{}({:?})", stringify!(#name), self.to_repr()) } } @@ -982,7 +982,7 @@ fn prime_field_impl( impl ::core::fmt::Display for #name { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({})", stringify!(#name), self.into_repr()) + write!(f, "{}({})", stringify!(#name), self.to_repr()) } } @@ -997,13 +997,13 @@ fn prime_field_impl( impl From<#name> for #repr { fn from(e: #name) -> #repr { - e.into_repr() + e.to_repr() } } impl<'a> From<&'a #name> for #repr { fn from(e: &'a #name) -> #repr { - e.into_repr() + e.to_repr() } } @@ -1153,8 +1153,8 @@ fn prime_field_impl( #from_repr_impl } - fn into_repr(&self) -> #repr { - #into_repr_impl + fn to_repr(&self) -> #repr { + #to_repr_impl } #[inline(always)] diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 2175de09af..16e0bec5b3 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -181,7 +181,7 @@ pub trait PrimeField: Field + From { /// /// The endianness of the byte representation is defined by /// [`PrimeField::ReprEndianness`]. - fn into_repr(&self) -> Self::Repr; + fn to_repr(&self) -> Self::Repr; /// Returns true iff this element is odd. fn is_odd(&self) -> bool; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 66a76c0965..75fc46f878 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -90,7 +90,7 @@ fn random_wnaf_tests() { g1.mul_assign(s); wnaf_table(&mut table, g, w); - wnaf_form(&mut wnaf, s.into_repr(), w); + wnaf_form(&mut wnaf, s.to_repr(), w); let g2 = wnaf_exp(&table, &wnaf); assert_eq!(g1, g2); diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 261b301d2a..57f780d305 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -149,7 +149,7 @@ impl Wnaf<(), Vec, Vec> { let window_size = G::recommended_wnaf_for_scalar(&scalar); // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar.into_repr(), window_size); + wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); // Return a Wnaf object that mutably borrows the base storage location, but // immutably borrows the computed wNAF form scalar location. @@ -203,7 +203,7 @@ impl>> Wnaf { where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar.into_repr(), self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } } diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs index f2a981f875..417ec9f8c8 100644 --- a/pairing/benches/bls12_381/fq.rs +++ b/pairing/benches/bls12_381/fq.rs @@ -155,7 +155,7 @@ fn bench_fq_sqrt(c: &mut Criterion) { }); } -fn bench_fq_into_repr(c: &mut Criterion) { +fn bench_fq_to_repr(c: &mut Criterion) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -166,10 +166,10 @@ fn bench_fq_into_repr(c: &mut Criterion) { let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); let mut count = 0; - c.bench_function("Fq::into_repr", |b| { + c.bench_function("Fq::to_repr", |b| { b.iter(|| { count = (count + 1) % SAMPLES; - v[count].into_repr() + v[count].to_repr() }) }); } @@ -183,7 +183,7 @@ fn bench_fq_from_repr(c: &mut Criterion) { ]); let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) + .map(|_| Fq::random(&mut rng).to_repr()) .collect(); let mut count = 0; @@ -204,6 +204,6 @@ criterion_group!( bench_fq_invert, bench_fq_neg, bench_fq_sqrt, - bench_fq_into_repr, + bench_fq_to_repr, bench_fq_from_repr, ); diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs index f3aa749675..468d68eb45 100644 --- a/pairing/benches/bls12_381/fr.rs +++ b/pairing/benches/bls12_381/fr.rs @@ -155,7 +155,7 @@ fn bench_fr_sqrt(c: &mut Criterion) { }); } -fn bench_fr_into_repr(c: &mut Criterion) { +fn bench_fr_to_repr(c: &mut Criterion) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -166,10 +166,10 @@ fn bench_fr_into_repr(c: &mut Criterion) { let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); let mut count = 0; - c.bench_function("Fr::into_repr", |b| { + c.bench_function("Fr::to_repr", |b| { b.iter(|| { count = (count + 1) % SAMPLES; - v[count].into_repr() + v[count].to_repr() }) }); } @@ -183,7 +183,7 @@ fn bench_fr_from_repr(c: &mut Criterion) { ]); let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) + .map(|_| Fr::random(&mut rng).to_repr()) .collect(); let mut count = 0; @@ -204,6 +204,6 @@ criterion_group!( bench_fr_invert, bench_fr_neg, bench_fr_sqrt, - bench_fr_into_repr, + bench_fr_to_repr, bench_fr_from_repr, ); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index d70d950f94..1a3f141915 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -872,8 +872,8 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - res.0[..48].copy_from_slice(&affine.x.into_repr().0); - res.0[48..].copy_from_slice(&affine.y.into_repr().0); + res.0[..48].copy_from_slice(&affine.x.to_repr().0); + res.0[48..].copy_from_slice(&affine.y.to_repr().0); } res @@ -969,7 +969,7 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - res.0 = affine.x.into_repr().0; + res.0 = affine.x.to_repr().0; let negy = affine.y.neg(); @@ -1494,10 +1494,10 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - res.0[0..48].copy_from_slice(&affine.x.c1.into_repr().0); - res.0[48..96].copy_from_slice(&affine.x.c0.into_repr().0); - res.0[96..144].copy_from_slice(&affine.y.c1.into_repr().0); - res.0[144..192].copy_from_slice(&affine.y.c0.into_repr().0); + res.0[0..48].copy_from_slice(&affine.x.c1.to_repr().0); + res.0[48..96].copy_from_slice(&affine.x.c0.to_repr().0); + res.0[96..144].copy_from_slice(&affine.y.c1.to_repr().0); + res.0[144..192].copy_from_slice(&affine.y.c0.to_repr().0); } res @@ -1608,8 +1608,8 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - res.0[..48].copy_from_slice(&affine.x.c1.into_repr().0); - res.0[48..].copy_from_slice(&affine.x.c0.into_repr().0); + res.0[..48].copy_from_slice(&affine.x.c1.to_repr().0); + res.0[48..].copy_from_slice(&affine.x.c0.to_repr().0); let negy = affine.y.neg(); diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index a84046712b..21ae050f28 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1687,7 +1687,7 @@ fn test_fq_sqrt() { } #[test] -fn test_fq_from_into_repr() { +fn test_fq_from_to_repr() { // q + 1 should not be in the field assert!(Fq::from_repr(FqRepr([ 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, @@ -1722,7 +1722,7 @@ fn test_fq_from_into_repr() { 0x17, 0x91, 0x4c, ]); a_fq.mul_assign(&b_fq); - assert_eq!(a_fq.into_repr(), c); + assert_eq!(a_fq.to_repr(), c); // Zero should be in the field. assert!(Fq::from_repr(FqRepr([0; 48])).unwrap().is_zero()); @@ -1735,7 +1735,7 @@ fn test_fq_from_into_repr() { for _ in 0..1000 { // Try to turn Fq elements into representations and back again, and compare. let a = Fq::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FqRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fq::from_repr(a_repr).unwrap(); diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 3e2e1f69e7..9bab427f4c 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -472,7 +472,7 @@ fn test_fr_sqrt() { } #[test] -fn test_fr_from_into_repr() { +fn test_fr_from_to_repr() { // r + 1 should not be in the field assert!(Fr::from_repr(FrRepr([ 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, @@ -503,7 +503,7 @@ fn test_fr_from_into_repr() { 0x61, 0x71, ]); a_fr.mul_assign(&b_fr); - assert_eq!(a_fr.into_repr(), c); + assert_eq!(a_fr.to_repr(), c); // Zero should be in the field. assert!(Fr::from_repr(FrRepr([0; 32])).unwrap().is_zero()); @@ -516,7 +516,7 @@ fn test_fr_from_into_repr() { for _ in 0..1000 { // Try to turn Fr elements into representations and back again, and compare. let a = Fr::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FrRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fr::from_repr(a_repr).unwrap(); diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 6d8cc927f7..e8663199f4 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -172,7 +172,7 @@ fn test_g1_uncompressed_invalid_vectors() { } { - let m = Fq::zero().into_repr(); + let m = Fq::zero().to_repr(); let mut o = o; o.as_mut()[..48].copy_from_slice(m.as_ref()); @@ -198,8 +198,8 @@ fn test_g1_uncompressed_invalid_vectors() { let y = y.unwrap(); // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.into_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(y.into_repr().as_ref()); + o.as_mut()[..48].copy_from_slice(x.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(y.to_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -310,7 +310,7 @@ fn test_g2_uncompressed_invalid_vectors() { } { - let m = Fq::zero().into_repr(); + let m = Fq::zero().to_repr(); let mut o = o; o.as_mut()[..48].copy_from_slice(m.as_ref()); @@ -340,10 +340,10 @@ fn test_g2_uncompressed_invalid_vectors() { let y = y.unwrap(); // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); - o.as_mut()[48..96].copy_from_slice(x.c0.into_repr().as_ref()); - o.as_mut()[96..144].copy_from_slice(y.c1.into_repr().as_ref()); - o.as_mut()[144..].copy_from_slice(y.c0.into_repr().as_ref()); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..96].copy_from_slice(x.c0.to_repr().as_ref()); + o.as_mut()[96..144].copy_from_slice(y.c1.to_repr().as_ref()); + o.as_mut()[144..].copy_from_slice(y.c0.to_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -433,7 +433,7 @@ fn test_g1_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { x.add_assign(&Fq::one()); } else { - o.as_mut().copy_from_slice(x.into_repr().as_ref()); + o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -456,7 +456,7 @@ fn test_g1_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut().copy_from_slice(x.into_repr().as_ref()); + o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { @@ -565,8 +565,8 @@ fn test_g2_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { x.add_assign(&Fq2::one()); } else { - o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(x.c0.into_repr().as_ref()); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -595,8 +595,8 @@ fn test_g2_compressed_invalid_vectors() { if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.c1.into_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(x.c0.into_repr().as_ref()); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index d4efb6dc77..e9f0570420 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -130,7 +130,7 @@ fn random_bilinearity_tests() { let mut cd = c; cd.mul_assign(&d); - let mut cd = cd.into_repr(); + let mut cd = cd.to_repr(); ::ReprEndianness::toggle_little_endian(&mut cd); use byteorder::ByteOrder; diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs index ca6112ed97..bdaffaa83c 100644 --- a/pairing/src/tests/repr.rs +++ b/pairing/src/tests/repr.rs @@ -15,7 +15,7 @@ fn random_encoding_tests() { for _ in 0..1000 { let r = P::random(&mut rng); - let v = r.into_repr(); + let v = r.to_repr(); let rdecoded = P::from_repr(v).unwrap(); assert_eq!(r, rdecoded); diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index d1f9bcd4db..4099e76762 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -36,7 +36,7 @@ fn scan_output( let ct = output.ciphertext; // Increment tree and witnesses - let node = Node::new(cmu.into_repr()); + let node = Node::new(cmu.to_repr()); for witness in existing_witnesses { witness.append(node).unwrap(); } @@ -207,7 +207,7 @@ mod tests { }; let fake_cmu = { let fake_cmu = Fr::random(rng); - fake_cmu.into_repr().as_ref().to_owned() + fake_cmu.to_repr().as_ref().to_owned() }; let fake_epk = { let mut buffer = vec![0; 64]; @@ -262,7 +262,7 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm(&JUBJUB).into_repr().as_ref().to_owned(); + let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned(); let mut epk = vec![]; encryptor.epk().write(&mut epk).unwrap(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index c4d6c804cd..612fbf56d7 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -172,7 +172,7 @@ impl Point { assert_eq!(E::Fr::NUM_BITS, 255); - let mut y_repr = y.into_repr(); + let mut y_repr = y.to_repr(); if x.is_odd() { y_repr.as_mut()[31] |= 0x80; } diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 816c896e77..fc82d75407 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -122,7 +122,7 @@ impl ConstantTimeEq for Fs { impl ::std::fmt::Display for Fs { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fs({})", self.into_repr()) + write!(f, "Fs({})", self.to_repr()) } } @@ -137,13 +137,13 @@ impl From for Fs { impl From for FsRepr { fn from(e: Fs) -> FsRepr { - e.into_repr() + e.to_repr() } } impl<'a> From<&'a Fs> for FsRepr { fn from(e: &'a Fs) -> FsRepr { - e.into_repr() + e.to_repr() } } @@ -325,7 +325,7 @@ impl PrimeField for Fs { } } - fn into_repr(&self) -> FsRepr { + fn to_repr(&self) -> FsRepr { let mut r = *self; r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); @@ -1095,7 +1095,7 @@ fn test_fs_sqrt() { } #[test] -fn test_fs_from_into_repr() { +fn test_fs_from_to_repr() { // r + 1 should not be in the field assert!(Fs::from_repr(FsRepr([ 0xb8, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, @@ -1140,7 +1140,7 @@ fn test_fs_from_into_repr() { for _ in 0..1000 { // Try to turn Fs elements into representations and back again, and compare. let a = Fs::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FsRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fs::from_repr(a_repr).unwrap(); diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index 6eeb1f5b03..a8b5274690 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -374,7 +374,7 @@ fn test_jubjub_params(params: &E::Params) { let max = { // Grab char - 1 in little endian. - let mut tmp = (-E::Fs::one()).into_repr(); + let mut tmp = (-E::Fs::one()).to_repr(); ::ReprEndianness::toggle_little_endian(&mut tmp); // Shift right by 1 bit. @@ -399,8 +399,8 @@ fn test_jubjub_params(params: &E::Params) { pacc += &tmp; nacc -= &tmp; // The first subtraction wraps intentionally. - let mut pacc_repr = pacc.into_repr(); - let mut nacc_repr = nacc.into_repr(); + let mut pacc_repr = pacc.to_repr(); + let mut nacc_repr = nacc.to_repr(); ::ReprEndianness::toggle_little_endian(&mut pacc_repr); ::ReprEndianness::toggle_little_endian(&mut nacc_repr); diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index 2f067a2e41..50c2de5cee 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -91,8 +91,8 @@ impl ExpandedSpendingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.ask.into_repr().as_ref())?; - writer.write_all(self.nsk.into_repr().as_ref())?; + writer.write_all(self.ask.to_repr().as_ref())?; + writer.write_all(self.nsk.to_repr().as_ref())?; writer.write_all(&self.ovk.0)?; Ok(()) diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index a3fc1fca72..9aa3ed5831 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -211,13 +211,13 @@ impl CommitmentTree { /// /// let mut tree = CommitmentTree::::new(); /// -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); +/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); +/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); /// let mut witness = IncrementalWitness::from_tree(&tree); /// assert_eq!(witness.position(), 1); /// assert_eq!(tree.root(), witness.root()); /// -/// let cmu = Node::new(Fr::random(&mut rng).into_repr()); +/// let cmu = Node::new(Fr::random(&mut rng).to_repr()); /// tree.append(cmu); /// witness.append(cmu); /// assert_eq!(tree.root(), witness.root()); diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 539ee643f3..1ad6cce2b1 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -193,7 +193,7 @@ fn prf_ock( let mut ock_input = [0u8; 128]; ock_input[0..32].copy_from_slice(&ovk.0); cv.write(&mut ock_input[32..64]).unwrap(); - ock_input[64..96].copy_from_slice(cmu.into_repr().as_ref()); + ock_input[64..96].copy_from_slice(cmu.to_repr().as_ref()); epk.write(&mut ock_input[96..128]).unwrap(); Blake2bParams::new() @@ -303,7 +303,7 @@ impl SaplingNoteEncryption { (&mut input[12..20]) .write_u64::(self.note.value) .unwrap(); - input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.into_repr().as_ref()); + input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.to_repr().as_ref()); input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; @@ -327,7 +327,7 @@ impl SaplingNoteEncryption { let mut input = [0u8; OUT_PLAINTEXT_SIZE]; self.note.pk_d.write(&mut input[0..32]).unwrap(); - input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.into_repr().as_ref()); + input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref()); let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; assert_eq!( @@ -366,7 +366,7 @@ fn parse_note_plaintext_without_memo( let diversifier = Diversifier(d); let pk_d = diversifier .g_d::(&JUBJUB)? - .mul(ivk.into_repr(), &JUBJUB); + .mul(ivk.to_repr(), &JUBJUB); let to = PaymentAddress::from_parts(diversifier, pk_d)?; let note = to.create_note(v, rcm, &JUBJUB).unwrap(); @@ -525,7 +525,7 @@ pub fn try_sapling_output_recovery( let diversifier = Diversifier(d); if diversifier .g_d::(&JUBJUB)? - .mul(esk.into_repr(), &JUBJUB) + .mul(esk.to_repr(), &JUBJUB) != *epk { // Published epk doesn't match calculated epk diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index 5d39cd6342..747ffcb8e3 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -89,7 +89,7 @@ where let window = JubjubBls12::pedersen_hash_exp_window_size() as usize; let window_mask = (1u64 << window) - 1; - let mut acc = acc.into_repr(); + let mut acc = acc.to_repr(); ::ReprEndianness::toggle_little_endian(&mut acc); let num_limbs: usize = acc.as_ref().len() / 8; let mut limbs = vec![0u64; num_limbs + 1]; diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index c816ddfdd5..c8088f62de 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -20,7 +20,7 @@ fn read_scalar(mut reader: R) -> io::Result { } fn write_scalar(s: &E::Fs, mut writer: W) -> io::Result<()> { - writer.write_all(s.into_repr().as_ref()) + writer.write_all(s.to_repr().as_ref()) } fn h_star(a: &[u8], b: &[u8]) -> E::Fs { diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 4582fe663f..0251803078 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -45,7 +45,7 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { ) .to_xy() .0 - .into_repr() + .to_repr() } /// A node within the Sapling commitment tree. @@ -79,7 +79,7 @@ impl Hashable for Node { fn blank() -> Self { Node { - repr: Note::::uncommitted().into_repr(), + repr: Note::::uncommitted().to_repr(), } } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index fda58ee846..18510a0c27 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -745,7 +745,7 @@ mod tests { let note1 = to .create_note(50000, Fs::random(&mut rng), &JUBJUB) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).into_repr()); + let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); let mut tree = CommitmentTree::new(); tree.append(cm1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); @@ -844,7 +844,7 @@ mod tests { let note1 = to .create_note(59999, Fs::random(&mut rng), &JUBJUB) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).into_repr()); + let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); let mut tree = CommitmentTree::new(); tree.append(cm1).unwrap(); let mut witness1 = IncrementalWitness::from_tree(&tree); @@ -882,7 +882,7 @@ mod tests { } let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap(); - let cm2 = Node::new(note2.cm(&JUBJUB).into_repr()); + let cm2 = Node::new(note2.cm(&JUBJUB).to_repr()); tree.append(cm2).unwrap(); witness1.append(cm2).unwrap(); let witness2 = IncrementalWitness::from_tree(&tree); diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index d53ee7f154..25baa28fee 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -176,7 +176,7 @@ impl SpendDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - writer.write_all(self.anchor.into_repr().as_ref())?; + writer.write_all(self.anchor.to_repr().as_ref())?; writer.write_all(&self.nullifier)?; self.rk.write(&mut writer)?; writer.write_all(&self.zkproof)?; @@ -254,7 +254,7 @@ impl OutputDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - writer.write_all(self.cmu.into_repr().as_ref())?; + writer.write_all(self.cmu.to_repr().as_ref())?; self.ephemeral_key.write(&mut writer)?; writer.write_all(&self.enc_ciphertext)?; writer.write_all(&self.out_ciphertext)?; diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index c77c2d0a17..89ee19270a 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -128,7 +128,7 @@ fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { s_spend.cv.write(&mut data).unwrap(); - data.extend_from_slice(s_spend.anchor.into_repr().as_ref()); + data.extend_from_slice(s_spend.anchor.to_repr().as_ref()); data.extend_from_slice(&s_spend.nullifier); s_spend.rk.write(&mut data).unwrap(); data.extend_from_slice(&s_spend.zkproof); diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index a02457f64c..1271a2d25e 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -1014,8 +1014,8 @@ mod tests { let xsk = &xsks[j]; let tv = &test_vectors[j]; - assert_eq!(xsk.expsk.ask.into_repr().as_ref(), tv.ask.unwrap()); - assert_eq!(xsk.expsk.nsk.into_repr().as_ref(), tv.nsk.unwrap()); + assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); @@ -1040,7 +1040,7 @@ mod tests { assert_eq!(xfvk.dk.0, tv.dk); assert_eq!(xfvk.chain_code.0, tv.c); - assert_eq!(xfvk.fvk.vk.ivk().into_repr().as_ref(), tv.ivk); + assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk); let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 59eb7614fe..d287e1bd9e 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -769,7 +769,7 @@ mod test { let q = p.mul(s, params); let (x1, y1) = q.to_xy(); - let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); @@ -822,7 +822,7 @@ mod test { y: num_y0, }; - let mut s_bits = BitIterator::::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 5e6c05f1b6..fe20e4fde0 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -615,8 +615,8 @@ fn test_input_circuit_with_bls12_381() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); lhs.reverse(); rhs.reverse(); @@ -799,8 +799,8 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); lhs.reverse(); rhs.reverse(); From d480a3840a153439fbe2d8bb6101a95cd95c40e0 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 13 May 2020 22:36:21 +0800 Subject: [PATCH 020/210] Hard-code Sapling circuit hashes in zcash_proofs crate Define the spend_hash, output_hash, sprout_hash circuit hashes as constants in the load_parameters function, so we don't have to take them as function arguments. --- zcash_proofs/src/lib.rs | 14 ++++++++------ zcash_proofs/src/prover.rs | 13 ++----------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 1e8ceb2fe0..6bbdf2c33c 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -22,11 +22,8 @@ pub mod prover; pub fn load_parameters( spend_path: &Path, - spend_hash: &str, output_path: &Path, - output_hash: &str, sprout_path: Option<&Path>, - sprout_hash: Option<&str>, ) -> ( Parameters, PreparedVerifyingKey, @@ -34,6 +31,11 @@ pub fn load_parameters( PreparedVerifyingKey, Option>, ) { + // Sapling circuit hashes + const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; + const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; + const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; + // Load from each of the paths let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file"); let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file"); @@ -74,15 +76,15 @@ pub fn load_parameters( .expect("couldn't finish reading Sprout groth16 parameter file"); } - if spend_fs.into_hash() != spend_hash { + if spend_fs.into_hash() != SAPLING_SPEND_HASH { panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if output_fs.into_hash() != output_hash { + if output_fs.into_hash() != SAPLING_OUTPUT_HASH { panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if sprout_fs.map(|fs| fs.into_hash()) != sprout_hash.map(|h| h.to_owned()) { + if sprout_fs.map(|fs| fs.into_hash()) != Some(SPROUT_HASH.to_owned()) { panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index c4608f7030..3dd665a7da 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -19,9 +19,6 @@ use zcash_primitives::{ use crate::{load_parameters, sapling::SaplingProvingContext}; -const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; -const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; - /// An implementation of [`TxProver`] using Sapling Spend and Output parameters from /// locally-accessible paths. pub struct LocalTxProver { @@ -50,14 +47,8 @@ impl LocalTxProver { /// This function will panic if the paths do not point to valid parameter files with /// the expected hashes. pub fn new(spend_path: &Path, output_path: &Path) -> Self { - let (spend_params, spend_vk, output_params, _, _) = load_parameters( - spend_path, - SAPLING_SPEND_HASH, - output_path, - SAPLING_OUTPUT_HASH, - None, - None, - ); + let (spend_params, spend_vk, output_params, _, _) = + load_parameters(spend_path, output_path, None); LocalTxProver { spend_params, spend_vk, From 6845154d88f6d4339aa8a04c8e454b4dac1d554f Mon Sep 17 00:00:00 2001 From: ying tong Date: Thu, 14 May 2020 11:30:13 +0800 Subject: [PATCH 021/210] Update zcash_proofs/src/lib.rs Co-authored-by: str4d --- zcash_proofs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 6bbdf2c33c..03c3efdc7d 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -84,7 +84,7 @@ pub fn load_parameters( panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if sprout_fs.map(|fs| fs.into_hash()) != Some(SPROUT_HASH.to_owned()) { + if sprout_fs.map(|fs| fs.into_hash() != SPROUT_HASH).unwrap_or(false) { panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } From f446b45af5f1d3ea129698821348310e92e9961e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 16:06:58 +1200 Subject: [PATCH 022/210] cargo fmt --- zcash_proofs/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 03c3efdc7d..7faca6c35c 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -84,7 +84,10 @@ pub fn load_parameters( panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if sprout_fs.map(|fs| fs.into_hash() != SPROUT_HASH).unwrap_or(false) { + if sprout_fs + .map(|fs| fs.into_hash() != SPROUT_HASH) + .unwrap_or(false) + { panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } From d7d49285d8295c9e233b7364f9fd19c3600546ac Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 Apr 2020 14:32:44 +1200 Subject: [PATCH 023/210] Remove explicit std::marker from Sized bound Sized is always part of the prelude, and binding on std causes compilation issues for no-std crates. --- bellman/src/groth16/tests/dummy_engine.rs | 4 ++-- ff/ff_derive/src/lib.rs | 2 +- ff/src/lib.rs | 2 +- group/src/lib.rs | 2 +- pairing/src/bls12_381/ec.rs | 2 +- pairing/src/bls12_381/fq12.rs | 2 +- pairing/src/bls12_381/fq2.rs | 2 +- pairing/src/bls12_381/fq6.rs | 2 +- zcash_primitives/src/jubjub/fs.rs | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index fccf5b00b5..c1bf516608 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -166,7 +166,7 @@ impl Shr for Fr { } impl Field for Fr { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fr(Wrapping(rng.next_u32()) % MODULUS_R) } @@ -358,7 +358,7 @@ impl CurveProjective for Fr { type Scalar = Fr; type Engine = DummyEngine; - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { ::random(rng) } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index f04ecfa13a..7e3a4abac8 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -1188,7 +1188,7 @@ fn prime_field_impl( impl ::ff::Field for #name { /// Computes a uniformly random element using rejection sampling. - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let mut tmp = { let mut repr = [0u64; #limbs]; diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 16e0bec5b3..3382262ee1 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -50,7 +50,7 @@ pub trait Field: + for<'a> SubAssign<&'a Self> { /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self; /// Returns the zero element of the field, the additive identity. fn zero() -> Self; diff --git a/group/src/lib.rs b/group/src/lib.rs index a330d1495a..05afa6f09c 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -52,7 +52,7 @@ pub trait CurveProjective: type Affine: CurveAffine; /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self; /// Returns the additive identity. fn zero() -> Self; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 1a3f141915..0715444910 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -515,7 +515,7 @@ macro_rules! curve_impl { type Base = $basefield; type Affine = $affine; - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let x = $basefield::random(rng); let greatest = rng.next_u32() % 2 != 0; diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs index 3cab598cfd..56632cbd4f 100644 --- a/pairing/src/bls12_381/fq12.rs +++ b/pairing/src/bls12_381/fq12.rs @@ -177,7 +177,7 @@ impl MulAssign for Fq12 { } impl Field for Fq12 { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fq12 { c0: Fq6::random(rng), c1: Fq6::random(rng), diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 38b48bce46..335664d584 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -185,7 +185,7 @@ impl MulAssign for Fq2 { } impl Field for Fq2 { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fq2 { c0: Fq::random(rng), c1: Fq::random(rng), diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs index b0183df870..52f7aaab42 100644 --- a/pairing/src/bls12_381/fq6.rs +++ b/pairing/src/bls12_381/fq6.rs @@ -278,7 +278,7 @@ impl MulAssign for Fq6 { } impl Field for Fq6 { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fq6 { c0: Fq2::random(rng), c1: Fq2::random(rng), diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index fc82d75407..d7e164efa4 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -362,7 +362,7 @@ impl PrimeField for Fs { } impl Field for Fs { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let mut tmp = { let mut repr = [0u64; 4]; From 4f2220fbb1e57ba185e7d8b45c8d567161e207df Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 May 2020 13:40:44 +1200 Subject: [PATCH 024/210] group: Rename zero and one to identity and generator --- bellman/src/domain.rs | 2 +- bellman/src/groth16/generator.rs | 20 +++--- bellman/src/groth16/mod.rs | 12 ++-- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 12 ++-- bellman/src/multiexp.rs | 10 +-- group/src/lib.rs | 12 ++-- group/src/tests/mod.rs | 50 ++++++++------ group/src/wnaf.rs | 2 +- pairing/src/bls12_381/ec.rs | 84 +++++++++++------------ pairing/src/bls12_381/mod.rs | 6 +- pairing/src/bls12_381/tests/mod.rs | 22 +++--- pairing/src/tests/engine.rs | 4 +- 13 files changed, 122 insertions(+), 116 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index be97c2037b..bafa59633b 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -218,7 +218,7 @@ impl Clone for Point { impl Group for Point { fn group_zero() -> Self { - Point(G::zero()) + Point(G::identity()) } fn group_mul_assign(&mut self, by: &G::Scalar) { self.0.mul_assign(by.to_repr()); diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 02efc21bd6..264d3ab2ad 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -234,7 +234,7 @@ where let worker = Worker::new(); - let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1]; + let mut h = vec![E::G1::identity(); powers_of_tau.as_ref().len() - 1]; { // Compute powers of tau { @@ -287,11 +287,11 @@ where powers_of_tau.ifft(&worker); let powers_of_tau = powers_of_tau.into_coeffs(); - let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; - let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; - let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux]; - let mut ic = vec![E::G1::zero(); assembly.num_inputs]; - let mut l = vec![E::G1::zero(); assembly.num_aux]; + let mut a = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![E::G2::identity(); assembly.num_inputs + assembly.num_aux]; + let mut ic = vec![E::G1::identity(); assembly.num_inputs]; + let mut l = vec![E::G1::identity(); assembly.num_aux]; fn eval( // wNAF window tables @@ -446,7 +446,7 @@ where // Don't allow any elements be unconstrained, so that // the L query is always fully dense. for e in l.iter() { - if e.is_zero() { + if e.is_identity() { return Err(SynthesisError::UnconstrainedVariable); } } @@ -472,19 +472,19 @@ where // Filter points at infinity away from A/B queries a: Arc::new( a.into_iter() - .filter(|e| !e.is_zero()) + .filter(|e| !e.is_identity()) .map(|e| e.into_affine()) .collect(), ), b_g1: Arc::new( b_g1.into_iter() - .filter(|e| !e.is_zero()) + .filter(|e| !e.is_identity()) .map(|e| e.into_affine()) .collect(), ), b_g2: Arc::new( b_g2.into_iter() - .filter(|e| !e.is_zero()) + .filter(|e| !e.is_identity()) .map(|e| e.into_affine()) .collect(), ), diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 6f5af85fc7..b338b0c62c 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -54,7 +54,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -69,7 +69,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -84,7 +84,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -198,7 +198,7 @@ impl VerifyingKey { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -303,7 +303,7 @@ impl Parameters { } .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -325,7 +325,7 @@ impl Parameters { } .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_zero() { + if e.is_identity() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 34abbb4ea9..97707fb09d 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -295,7 +295,7 @@ where ); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); - if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { + if vk.delta_g1.is_identity() || vk.delta_g2.is_identity() { // If this element is zero, someone is trying to perform a // subversion-CRS attack. return Err(SynthesisError::UnexpectedIdentity); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index c1bf516608..c95a7510aa 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -362,15 +362,15 @@ impl CurveProjective for Fr { ::random(rng) } - fn zero() -> Self { + fn identity() -> Self { ::zero() } - fn one() -> Self { + fn generator() -> Self { ::one() } - fn is_zero(&self) -> bool { + fn is_identity(&self) -> bool { ::is_zero(self) } @@ -450,15 +450,15 @@ impl CurveAffine for Fr { type Scalar = Fr; type Engine = DummyEngine; - fn zero() -> Self { + fn identity() -> Self { ::zero() } - fn one() -> Self { + fn generator() -> Self { ::one() } - fn is_zero(&self) -> bool { + fn is_identity(&self) -> bool { ::is_zero(self) } diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index deed9fa05f..948086b46d 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -55,7 +55,7 @@ impl Source for (Arc>, usize) { .into()); } - if self.0[self.1].is_zero() { + if self.0[self.1].is_identity() { return Err(SynthesisError::UnexpectedIdentity); } @@ -173,13 +173,13 @@ where pool.compute(move || { // Accumulate the result - let mut acc = G::zero(); + let mut acc = G::identity(); // Build a source for the bases let mut bases = bases.new(); // Create space for the buckets - let mut buckets = vec![G::zero(); (1 << c) - 1]; + let mut buckets = vec![G::identity(); (1 << c) - 1]; let one = ::Fr::one(); @@ -222,7 +222,7 @@ where // e.g. 3a + 2b + 1c = a + // (a) + b + // ((a) + b) + c - let mut running_sum = G::zero(); + let mut running_sum = G::identity(); for exp in buckets.into_iter().rev() { running_sum.add_assign(&exp); acc.add_assign(&running_sum); @@ -302,7 +302,7 @@ fn test_with_bls12() { ) -> G { assert_eq!(bases.len(), exponents.len()); - let mut acc = G::zero(); + let mut acc = G::identity(); for (base, exp) in bases.iter().zip(exponents.iter()) { AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.to_repr())); diff --git a/group/src/lib.rs b/group/src/lib.rs index 05afa6f09c..8f40d0a2cf 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -55,13 +55,13 @@ pub trait CurveProjective: fn random(rng: &mut R) -> Self; /// Returns the additive identity. - fn zero() -> Self; + fn identity() -> Self; /// Returns a fixed generator of unknown exponent. - fn one() -> Self; + fn generator() -> Self; /// Determines if this point is the point at infinity. - fn is_zero(&self) -> bool; + fn is_identity(&self) -> bool; /// Normalizes a slice of projective elements so that /// conversion to affine is cheap. @@ -112,14 +112,14 @@ pub trait CurveAffine: type Compressed: EncodedPoint; /// Returns the additive identity. - fn zero() -> Self; + fn identity() -> Self; /// Returns a fixed generator of unknown exponent. - fn one() -> Self; + fn generator() -> Self; /// Determines if this point represents the point at infinity; the /// additive identity. - fn is_zero(&self) -> bool; + fn is_identity(&self) -> bool; /// Performs scalar multiplication of this element with mixed addition. fn mul::Repr>>(&self, other: S) -> Self::Projective; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 75fc46f878..5540cc8c2f 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -11,33 +11,33 @@ pub fn curve_tests() { 0xe5, ]); - // Negation edge case with zero. + // Negation edge case with identity. { - let z = G::zero().neg(); - assert!(z.is_zero()); + let z = G::identity().neg(); + assert!(z.is_identity()); } - // Doubling edge case with zero. + // Doubling edge case with identity. { - let mut z = G::zero(); + let mut z = G::identity(); z.double(); - assert!(z.is_zero()); + assert!(z.is_identity()); } - // Addition edge cases with zero + // Addition edge cases with identity { let mut r = G::random(&mut rng); let rcopy = r; - r.add_assign(&G::zero()); + r.add_assign(&G::identity()); assert_eq!(r, rcopy); - r.add_assign(&G::Affine::zero()); + r.add_assign(&G::Affine::identity()); assert_eq!(r, rcopy); - let mut z = G::zero(); - z.add_assign(&G::zero()); - assert!(z.is_zero()); - z.add_assign(&G::Affine::zero()); - assert!(z.is_zero()); + let mut z = G::identity(); + z.add_assign(&G::identity()); + assert!(z.is_identity()); + z.add_assign(&G::Affine::identity()); + assert!(z.is_identity()); let mut z2 = z; z2.add_assign(&r); @@ -209,11 +209,11 @@ fn random_negation_tests() { let mut t3 = t1; t3.add_assign(&t2); - assert!(t3.is_zero()); + assert!(t3.is_identity()); let mut t4 = t1; t4.add_assign(&t2.into_affine()); - assert!(t4.is_zero()); + assert!(t4.is_identity()); assert_eq!(t1.neg(), t2); } @@ -313,7 +313,7 @@ fn random_addition_tests() { assert_eq!(aplusa, aplusamixed); } - let mut tmp = vec![G::zero(); 6]; + let mut tmp = vec![G::identity(); 6]; // (a + b) + c tmp[0] = a; @@ -390,7 +390,7 @@ fn random_transformation_tests() { let between = Uniform::new(0, 1000); // Sprinkle in some normalized points for _ in 0..5 { - v[between.sample(&mut rng)] = G::zero(); + v[between.sample(&mut rng)] = G::identity(); } for _ in 0..5 { let s = between.sample(&mut rng); @@ -418,13 +418,19 @@ fn random_encoding_tests() { ]); assert_eq!( - G::Affine::zero().into_uncompressed().into_affine().unwrap(), - G::Affine::zero() + G::Affine::identity() + .into_uncompressed() + .into_affine() + .unwrap(), + G::Affine::identity() ); assert_eq!( - G::Affine::zero().into_compressed().into_affine().unwrap(), - G::Affine::zero() + G::Affine::identity() + .into_compressed() + .into_affine() + .unwrap(), + G::Affine::identity() ); for _ in 0..1000 { diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 57f780d305..86f77e2ff3 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -80,7 +80,7 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize /// This function must be provided a `table` and `wnaf` that were constructed with /// the same window size; otherwise, it may panic or produce invalid results. pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { - let mut result = G::zero(); + let mut result = G::identity(); let mut found_one = false; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 0715444910..39032ae7d8 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -42,11 +42,11 @@ macro_rules! curve_impl { impl PartialEq for $projective { fn eq(&self, other: &$projective) -> bool { - if self.is_zero() { - return other.is_zero(); + if self.is_identity() { + return other.is_identity(); } - if other.is_zero() { + if other.is_identity() { return false; } @@ -82,7 +82,7 @@ macro_rules! curve_impl { impl $affine { fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { - let mut res = $projective::zero(); + let mut res = $projective::identity(); for i in bits { res.double(); if i { @@ -93,7 +93,7 @@ macro_rules! curve_impl { } fn mul_bits_u8>(&self, bits: BitIterator) -> $projective { - let mut res = $projective::zero(); + let mut res = $projective::identity(); for i in bits { res.double(); if i { @@ -126,7 +126,7 @@ macro_rules! curve_impl { } fn is_on_curve(&self) -> bool { - if self.is_zero() { + if self.is_identity() { true } else { // Check that the point is on the curve @@ -141,7 +141,7 @@ macro_rules! curve_impl { } fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { - self.mul($scalarfield::char()).is_zero() + self.mul($scalarfield::char()).is_identity() } } @@ -151,7 +151,7 @@ macro_rules! curve_impl { #[inline] fn neg(self) -> Self { let mut ret = self; - if !ret.is_zero() { + if !ret.is_identity() { ret.y = ret.y.neg(); } ret @@ -166,7 +166,7 @@ macro_rules! curve_impl { type Uncompressed = $uncompressed; type Compressed = $compressed; - fn zero() -> Self { + fn identity() -> Self { $affine { x: $basefield::zero(), y: $basefield::one(), @@ -174,11 +174,11 @@ macro_rules! curve_impl { } } - fn one() -> Self { + fn generator() -> Self { Self::get_generator() } - fn is_zero(&self) -> bool { + fn is_identity(&self) -> bool { self.infinity } @@ -212,7 +212,7 @@ macro_rules! curve_impl { #[inline] fn neg(self) -> Self { let mut ret = self; - if !ret.is_zero() { + if !ret.is_identity() { ret.y = ret.y.neg(); } ret @@ -241,12 +241,12 @@ macro_rules! curve_impl { impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective { fn add_assign(&mut self, other: &Self) { - if self.is_zero() { + if self.is_identity() { *self = *other; return; } - if other.is_zero() { + if other.is_identity() { return; } @@ -390,11 +390,11 @@ macro_rules! curve_impl { for $projective { fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { - if other.is_zero() { + if other.is_identity() { return; } - if self.is_zero() { + if self.is_identity() { self.x = other.x; self.y = other.y; self.z = $basefield::one(); @@ -524,7 +524,7 @@ macro_rules! curve_impl { if p.is_some().into() { let p = p.unwrap().scale_by_cofactor(); - if !p.is_zero() { + if !p.is_identity() { return p; } } @@ -533,7 +533,7 @@ macro_rules! curve_impl { // The point at infinity is always represented by // Z = 0. - fn zero() -> Self { + fn identity() -> Self { $projective { x: $basefield::zero(), y: $basefield::one(), @@ -541,18 +541,18 @@ macro_rules! curve_impl { } } - fn one() -> Self { - $affine::one().into() + fn generator() -> Self { + $affine::generator().into() } // The point at infinity is always represented by // Z = 0. - fn is_zero(&self) -> bool { + fn is_identity(&self) -> bool { self.z.is_zero() } fn is_normalized(&self) -> bool { - self.is_zero() || self.z == $basefield::one() + self.is_identity() || self.z == $basefield::one() } fn batch_normalization(v: &mut [Self]) { @@ -609,7 +609,7 @@ macro_rules! curve_impl { } fn double(&mut self) { - if self.is_zero() { + if self.is_identity() { return; } @@ -662,7 +662,7 @@ macro_rules! curve_impl { } fn mul_assign::Repr>>(&mut self, other: S) { - let mut res = Self::zero(); + let mut res = Self::identity(); let mut found_one = false; @@ -700,8 +700,8 @@ macro_rules! curve_impl { // coordinates with Z = 1. impl From<$affine> for $projective { fn from(p: $affine) -> $projective { - if p.is_zero() { - $projective::zero() + if p.is_identity() { + $projective::identity() } else { $projective { x: p.x, @@ -716,8 +716,8 @@ macro_rules! curve_impl { // coordinates as X/Z^2, Y/Z^3. impl From<$projective> for $affine { fn from(p: $projective) -> $affine { - if p.is_zero() { - $affine::zero() + if p.is_identity() { + $affine::identity() } else if p.z == $basefield::one() { // If Z is one, the point is already normalized. $affine { @@ -830,7 +830,7 @@ pub mod g1 { copy[0] &= 0x3f; if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::zero()) + Ok(G1Affine::identity()) } else { Err(GroupDecodingError::UnexpectedInformation) } @@ -867,7 +867,7 @@ pub mod g1 { fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); - if affine.is_zero() { + if affine.is_identity() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -937,7 +937,7 @@ pub mod g1 { copy[0] &= 0x3f; if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::zero()) + Ok(G1Affine::identity()) } else { Err(GroupDecodingError::UnexpectedInformation) } @@ -964,7 +964,7 @@ pub mod g1 { fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); - if affine.is_zero() { + if affine.is_identity() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -1043,8 +1043,8 @@ pub mod g1 { pub struct G1Prepared(pub(crate) G1Affine); impl G1Prepared { - pub fn is_zero(&self) -> bool { - self.0.is_zero() + pub fn is_identity(&self) -> bool { + self.0.is_identity() } pub fn from_affine(p: G1Affine) -> Self { @@ -1075,13 +1075,13 @@ pub mod g1 { assert!(!p.is_in_correct_subgroup_assuming_on_curve()); let g1 = p.scale_by_cofactor(); - if !g1.is_zero() { + if !g1.is_identity() { assert_eq!(i, 4); let g1 = G1Affine::from(g1); assert!(g1.is_in_correct_subgroup_assuming_on_curve()); - assert_eq!(g1, G1Affine::one()); + assert_eq!(g1, G1Affine::generator()); break; } } @@ -1440,7 +1440,7 @@ pub mod g2 { copy[0] &= 0x3f; if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::zero()) + Ok(G2Affine::identity()) } else { Err(GroupDecodingError::UnexpectedInformation) } @@ -1489,7 +1489,7 @@ pub mod g2 { fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); - if affine.is_zero() { + if affine.is_identity() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -1561,7 +1561,7 @@ pub mod g2 { copy[0] &= 0x3f; if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::zero()) + Ok(G2Affine::identity()) } else { Err(GroupDecodingError::UnexpectedInformation) } @@ -1603,7 +1603,7 @@ pub mod g2 { fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); - if affine.is_zero() { + if affine.is_identity() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -1728,12 +1728,12 @@ pub mod g2 { assert!(!p.is_in_correct_subgroup_assuming_on_curve()); let g2 = p.scale_by_cofactor(); - if !g2.is_zero() { + if !g2.is_identity() { assert_eq!(i, 2); let g2 = G2Affine::from(g2); assert!(g2.is_in_correct_subgroup_assuming_on_curve()); - assert_eq!(g2, G2Affine::one()); + assert_eq!(g2, G2Affine::generator()); break; } } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index afe9c826a1..748ba5f310 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -59,7 +59,7 @@ impl Engine for Bls12 { { let mut pairs = vec![]; for &(p, q) in i { - if !p.is_zero() && !q.is_zero() { + if !p.is_identity() && !q.is_identity() { pairs.push((p, q.coeffs.iter())); } } @@ -168,12 +168,12 @@ impl Engine for Bls12 { } impl G2Prepared { - pub fn is_zero(&self) -> bool { + pub fn is_identity(&self) -> bool { self.infinity } pub fn from_affine(q: G2Affine) -> Self { - if q.is_zero() { + if q.is_identity() { return G2Prepared { coeffs: vec![], infinity: true, diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index e8663199f4..734e6f3ccc 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -23,7 +23,7 @@ fn test_pairing_result_against_relic() { 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 */ - assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 { + assert_eq!(Bls12::pairing(G1::generator(), G2::generator()), Fq12 { c0: Fq6 { c0: Fq2 { c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(), @@ -56,7 +56,7 @@ fn test_pairing_result_against_relic() { } fn test_vectors>(expected: &[u8]) { - let mut e = G::zero(); + let mut e = G::identity(); let mut v = vec![]; { @@ -72,7 +72,7 @@ fn test_vectors>(expecte let decoded = decoded.into_affine().unwrap(); assert_eq!(e_affine, decoded); - e.add_assign(&G::one()); + e.add_assign(&G::generator()); } } @@ -102,7 +102,7 @@ fn test_g2_compressed_valid_vectors() { #[test] fn test_g1_uncompressed_invalid_vectors() { { - let z = G1Affine::zero().into_uncompressed(); + let z = G1Affine::identity().into_uncompressed(); { let mut z = z; @@ -135,7 +135,7 @@ fn test_g1_uncompressed_invalid_vectors() { } } - let o = G1Affine::one().into_uncompressed(); + let o = G1Affine::generator().into_uncompressed(); { let mut o = o; @@ -218,7 +218,7 @@ fn test_g1_uncompressed_invalid_vectors() { #[test] fn test_g2_uncompressed_invalid_vectors() { { - let z = G2Affine::zero().into_uncompressed(); + let z = G2Affine::identity().into_uncompressed(); { let mut z = z; @@ -251,7 +251,7 @@ fn test_g2_uncompressed_invalid_vectors() { } } - let o = G2Affine::one().into_uncompressed(); + let o = G2Affine::generator().into_uncompressed(); { let mut o = o; @@ -362,7 +362,7 @@ fn test_g2_uncompressed_invalid_vectors() { #[test] fn test_g1_compressed_invalid_vectors() { { - let z = G1Affine::zero().into_compressed(); + let z = G1Affine::identity().into_compressed(); { let mut z = z; @@ -395,7 +395,7 @@ fn test_g1_compressed_invalid_vectors() { } } - let o = G1Affine::one().into_compressed(); + let o = G1Affine::generator().into_compressed(); { let mut o = o; @@ -476,7 +476,7 @@ fn test_g1_compressed_invalid_vectors() { #[test] fn test_g2_compressed_invalid_vectors() { { - let z = G2Affine::zero().into_compressed(); + let z = G2Affine::identity().into_compressed(); { let mut z = z; @@ -509,7 +509,7 @@ fn test_g2_compressed_invalid_vectors() { } } - let o = G2Affine::one().into_compressed(); + let o = G2Affine::generator().into_compressed(); { let mut o = o; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index e9f0570420..f2277ba028 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -21,8 +21,8 @@ pub fn engine_tests() { } for _ in 0..1000 { - let z1 = E::G1Affine::zero().prepare(); - let z2 = E::G2Affine::zero().prepare(); + let z1 = E::G1Affine::identity().prepare(); + let z2 = E::G2Affine::identity().prepare(); let a = E::G1::random(&mut rng).into_affine().prepare(); let b = E::G2::random(&mut rng).into_affine().prepare(); From 8add78a327b91e0a2b147d144e54ad79fd0d9f77 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 May 2020 14:02:24 +1200 Subject: [PATCH 025/210] Make CurveProjective::double take &self and return Self --- bellman/src/groth16/tests/dummy_engine.rs | 4 +-- bellman/src/multiexp.rs | 2 +- group/src/lib.rs | 3 +- group/src/tests/mod.rs | 14 +++----- group/src/wnaf.rs | 5 ++- pairing/src/bls12_381/ec.rs | 43 +++++++++-------------- 6 files changed, 29 insertions(+), 42 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index c95a7510aa..60bb514a71 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -380,8 +380,8 @@ impl CurveProjective for Fr { true } - fn double(&mut self) { - self.0 = ::double(self).0; + fn double(&self) -> Self { + ::double(self) } fn mul_assign::Repr>>(&mut self, other: S) { diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 948086b46d..2c2be59d87 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -252,7 +252,7 @@ where )) .map(move |(this, mut higher): (_, G)| { for _ in 0..c { - higher.double(); + higher = higher.double(); } higher.add_assign(&this); diff --git a/group/src/lib.rs b/group/src/lib.rs index 8f40d0a2cf..b6fcc90afc 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -72,7 +72,8 @@ pub trait CurveProjective: fn is_normalized(&self) -> bool; /// Doubles this element. - fn double(&mut self); + #[must_use] + fn double(&self) -> Self; /// Performs scalar multiplication of this element. fn mul_assign::Repr>>(&mut self, other: S); diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 5540cc8c2f..ef4a0e2695 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -19,8 +19,7 @@ pub fn curve_tests() { // Doubling edge case with identity. { - let mut z = G::identity(); - z.double(); + let z = G::identity().double(); assert!(z.is_identity()); } @@ -230,13 +229,11 @@ fn random_doubling_tests() { let mut b = G::random(&mut rng); // 2(a + b) - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.double(); + let tmp1 = (a + b).double(); // 2a + 2b - a.double(); - b.double(); + a = a.double(); + b = b.double(); let mut tmp2 = a; tmp2.add_assign(&b); @@ -306,8 +303,7 @@ fn random_addition_tests() { let mut aplusamixed = a; aplusamixed.add_assign(&a.into_affine()); - let mut adouble = a; - adouble.double(); + let adouble = a.double(); assert_eq!(aplusa, adouble); assert_eq!(aplusa, aplusamixed); diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 86f77e2ff3..79efd021ad 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -9,8 +9,7 @@ pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, wi table.truncate(0); table.reserve(1 << (window - 1)); - let mut dbl = base; - dbl.double(); + let dbl = base.double(); for _ in 0..(1 << (window - 1)) { table.push(base); @@ -86,7 +85,7 @@ pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { for n in wnaf.iter().rev() { if found_one { - result.double(); + result = result.double(); } if *n != 0 { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 39032ae7d8..64c391cc27 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -84,7 +84,7 @@ macro_rules! curve_impl { fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { let mut res = $projective::identity(); for i in bits { - res.double(); + res = res.double(); if i { res.add_assign(self) } @@ -95,7 +95,7 @@ macro_rules! curve_impl { fn mul_bits_u8>(&self, bits: BitIterator) -> $projective { let mut res = $projective::identity(); for i in bits { - res.double(); + res = res.double(); if i { res.add_assign(self) } @@ -278,7 +278,7 @@ macro_rules! curve_impl { if u1 == u2 && s1 == s2 { // The two points are equal, so we double. - self.double(); + *self = self.double(); } else { // If we're adding -a and a together, self.z becomes zero as H becomes zero. @@ -417,7 +417,7 @@ macro_rules! curve_impl { if self.x == u2 && self.y == s2 { // The two points are equal, so we double. - self.double(); + *self = self.double(); } else { // If we're adding -a and a together, self.z becomes zero as H becomes zero. @@ -608,9 +608,9 @@ macro_rules! curve_impl { } } - fn double(&mut self) { + fn double(&self) -> Self { if self.is_identity() { - return; + return *self; } // Other than the point at infinity, no points on E or E' @@ -627,7 +627,7 @@ macro_rules! curve_impl { let b = self.y.square(); // C = B^2 - let mut c = b.square(); + let c = b.square(); // D = 2*((X1+B)2-A-C) let mut d = self.x; @@ -645,20 +645,15 @@ macro_rules! curve_impl { let f = e.square(); // Z3 = 2*Y1*Z1 - self.z.mul_assign(&self.y); - self.z = self.z.double(); + let z = self.z.double() * self.y; // X3 = F-2*D - self.x = f; - self.x.sub_assign(&d); - self.x.sub_assign(&d); + let x = f - d.double(); // Y3 = E*(D-X3)-8*C - self.y = d; - self.y.sub_assign(&self.x); - self.y.mul_assign(&e); - c = c.double().double().double(); - self.y.sub_assign(&c); + let y = e * (d - x) - c.double().double().double(); + + $projective { x, y, z } } fn mul_assign::Repr>>(&mut self, other: S) { @@ -668,7 +663,7 @@ macro_rules! curve_impl { for i in BitIterator::::new(other.into()) { if found_one { - res.double(); + res = res.double(); } else { found_one = i; } @@ -1228,7 +1223,7 @@ pub mod g1 { #[test] fn test_g1_doubling_correctness() { - let mut p = G1 { + let p = G1 { x: Fq::from_repr(FqRepr([ 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, @@ -1246,9 +1241,7 @@ pub mod g1 { z: Fq::one(), }; - p.double(); - - let p = G1Affine::from(p); + let p = G1Affine::from(p.double()); assert_eq!( p, @@ -1988,7 +1981,7 @@ pub mod g2 { #[test] fn test_g2_doubling_correctness() { - let mut p = G2 { + let p = G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, @@ -2024,9 +2017,7 @@ pub mod g2 { z: Fq2::one(), }; - p.double(); - - let p = G2Affine::from(p); + let p = G2Affine::from(p.double()); assert_eq!( p, From 7203a7a30a59f3d8af3030feeea78504a2dc3c43 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 May 2020 18:49:03 +1200 Subject: [PATCH 026/210] Remove Engine associated type from CurveAffine and CurveProjective The type Curve*::Engine::Fr is equivalent to Curve*::Scalar, making Engine a redundant associated type. --- bellman/src/domain.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 2 -- bellman/src/multiexp.rs | 15 +++++++++------ group/src/lib.rs | 4 +--- pairing/src/bls12_381/ec.rs | 6 ++---- pairing/src/lib.rs | 6 ++---- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index bafa59633b..748a15a751 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -216,7 +216,7 @@ impl Clone for Point { } } -impl Group for Point { +impl> Group for Point { fn group_zero() -> Self { Point(G::identity()) } diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 60bb514a71..841c05295d 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -356,7 +356,6 @@ impl CurveProjective for Fr { type Affine = Fr; type Base = Fr; type Scalar = Fr; - type Engine = DummyEngine; fn random(rng: &mut R) -> Self { ::random(rng) @@ -448,7 +447,6 @@ impl CurveAffine for Fr { type Projective = Fr; type Base = Fr; type Scalar = Fr; - type Engine = DummyEngine; fn identity() -> Self { ::zero() diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 2c2be59d87..881dd21c19 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -1,6 +1,6 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; -use ff::{Endianness, Field, PrimeField, ScalarEngine}; +use ff::{Endianness, Field, PrimeField}; use futures::Future; use group::{CurveAffine, CurveProjective}; use std::io; @@ -154,7 +154,7 @@ fn multiexp_inner( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr>>, + exponents: Arc>, mut skip: u32, c: u32, handle_trivial: bool, @@ -181,7 +181,7 @@ where // Create space for the buckets let mut buckets = vec![G::identity(); (1 << c) - 1]; - let one = ::Fr::one(); + let one = G::Scalar::one(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { @@ -196,7 +196,7 @@ where } } else { let mut exp = exp.to_repr(); - <::Fr as PrimeField>::ReprEndianness::toggle_little_endian(&mut exp); + ::ReprEndianness::toggle_little_endian(&mut exp); let exp = exp .as_ref() @@ -234,7 +234,7 @@ where skip += c; - if skip >= ::Fr::NUM_BITS { + if skip >= G::Scalar::NUM_BITS { // There isn't another region. Box::new(this) } else { @@ -269,7 +269,7 @@ pub fn multiexp( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr>>, + exponents: Arc>, ) -> Box> where for<'a> &'a Q: QueryDensity, @@ -293,6 +293,9 @@ where multiexp_inner(pool, bases, density_map, exponents, 0, c, true) } +#[cfg(all(test, feature = "pairing"))] +use ff::ScalarEngine; + #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { diff --git a/group/src/lib.rs b/group/src/lib.rs index b6fcc90afc..3b730f444d 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -1,7 +1,7 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField}; use rand::RngCore; use std::error::Error; use std::fmt; @@ -46,7 +46,6 @@ pub trait CurveProjective: + CurveOps<::Affine> + CurveOpsOwned<::Affine> { - type Engine: ScalarEngine; type Scalar: PrimeField; type Base: Field; type Affine: CurveAffine; @@ -105,7 +104,6 @@ pub trait CurveAffine: + 'static + Neg { - type Engine: ScalarEngine; type Scalar: PrimeField; type Base: Field; type Projective: CurveProjective; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 64c391cc27..8f0c993efa 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -159,7 +159,6 @@ macro_rules! curve_impl { } impl CurveAffine for $affine { - type Engine = Bls12; type Scalar = $scalarfield; type Base = $basefield; type Projective = $projective; @@ -510,7 +509,6 @@ macro_rules! curve_impl { } impl CurveProjective for $projective { - type Engine = Bls12; type Scalar = $scalarfield; type Base = $basefield; type Affine = $affine; @@ -746,7 +744,7 @@ macro_rules! curve_impl { } pub mod g1 { - use super::super::{Bls12, Fq, Fq12, FqRepr, Fr}; + use super::super::{Fq, Fq12, FqRepr, Fr}; use super::g2::G2Affine; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; @@ -1354,7 +1352,7 @@ pub mod g1 { } pub mod g2 { - use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr}; + use super::super::{Fq, Fq12, Fq2, FqRepr, Fr}; use super::g1::G1Affine; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 645c70db3a..e08eaacc88 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -29,14 +29,13 @@ use subtle::CtOption; /// of prime order `r`, and are equipped with a bilinear pairing function. pub trait Engine: ScalarEngine { /// The projective representation of an element in G1. - type G1: CurveProjective + type G1: CurveProjective + From + CurveOps + CurveOpsOwned; /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< - Engine = Self, Base = Self::Fq, Scalar = Self::Fr, Projective = Self::G1, @@ -45,14 +44,13 @@ pub trait Engine: ScalarEngine { > + From; /// The projective representation of an element in G2. - type G2: CurveProjective + type G2: CurveProjective + From + CurveOps + CurveOpsOwned; /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< - Engine = Self, Base = Self::Fqe, Scalar = Self::Fr, Projective = Self::G2, From 69f53febcfed8e695ac48747ab23d633f32cbe34 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 18:10:06 +1200 Subject: [PATCH 027/210] group: Introduce Group and PrimeGroup traits Group represents a cryptographic group with a large prime-order subgroup and a small cofactor. PrimeGroup further constrains the group to have a cofactor of one. --- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 16 +++++---- bellman/src/multiexp.rs | 1 + group/src/lib.rs | 43 +++++++++++------------ pairing/benches/bls12_381/ec.rs | 4 +-- pairing/benches/bls12_381/mod.rs | 2 +- pairing/src/bls12_381/ec.rs | 22 ++++++++---- pairing/src/bls12_381/tests/mod.rs | 2 +- pairing/src/tests/engine.rs | 2 +- 9 files changed, 53 insertions(+), 41 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 264d3ab2ad..b270ab3365 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::Field; -use group::{CurveAffine, CurveProjective, Wnaf}; +use group::{CurveAffine, CurveProjective, Group, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 841c05295d..611be9752b 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField, ScalarEngine}; -use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; +use group::{CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup}; use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; @@ -352,11 +352,7 @@ impl Engine for DummyEngine { } } -impl CurveProjective for Fr { - type Affine = Fr; - type Base = Fr; - type Scalar = Fr; - +impl Group for Fr { fn random(rng: &mut R) -> Self { ::random(rng) } @@ -372,6 +368,14 @@ impl CurveProjective for Fr { fn is_identity(&self) -> bool { ::is_zero(self) } +} + +impl PrimeGroup for Fr {} + +impl CurveProjective for Fr { + type Affine = Fr; + type Base = Fr; + type Scalar = Fr; fn batch_normalization(_: &mut [Self]) {} diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 881dd21c19..643f743534 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -314,6 +314,7 @@ fn test_with_bls12() { acc } + use group::Group; use pairing::{bls12_381::Bls12, Engine}; use rand; diff --git a/group/src/lib.rs b/group/src/lib.rs index 3b730f444d..977f48654f 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -12,6 +12,26 @@ pub mod tests; mod wnaf; pub use self::wnaf::Wnaf; +/// This trait represents an element of a cryptographic group. +pub trait Group: + Clone + Copy + fmt::Debug + fmt::Display + Eq + Sized + Send + Sync + 'static +{ + /// Returns an element chosen uniformly at random using a user-provided RNG. + fn random(rng: &mut R) -> Self; + + /// Returns the additive identity, also known as the "neutral element". + fn identity() -> Self; + + /// Returns a fixed generator of the prime-order subgroup. + fn generator() -> Self; + + /// Determines if this point is the identity. + fn is_identity(&self) -> bool; +} + +/// This trait represents an element of a prime-order cryptographic group. +pub trait PrimeGroup: Group {} + /// A helper trait for types implementing group addition. pub trait CurveOps: Add + Sub + AddAssign + SubAssign @@ -30,16 +50,7 @@ impl CurveOpsOwned for T where T: for<'r> CurveOps< /// Projective representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. pub trait CurveProjective: - PartialEq - + Eq - + Sized - + Copy - + Clone - + Send - + Sync - + fmt::Debug - + fmt::Display - + 'static + Group + Neg + CurveOps + CurveOpsOwned @@ -50,18 +61,6 @@ pub trait CurveProjective: type Base: Field; type Affine: CurveAffine; - /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point is the point at infinity. - fn is_identity(&self) -> bool; - /// Normalizes a slice of projective elements so that /// conversion to affine is cheap. fn batch_normalization(v: &mut [Self]); diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs index 28ce4bf849..d5ece12734 100644 --- a/pairing/benches/bls12_381/ec.rs +++ b/pairing/benches/bls12_381/ec.rs @@ -5,7 +5,7 @@ pub(crate) mod g1 { use std::ops::AddAssign; use ff::Field; - use group::CurveProjective; + use group::{CurveProjective, Group}; use pairing::bls12_381::*; fn bench_g1_mul_assign(c: &mut Criterion) { @@ -92,7 +92,7 @@ pub(crate) mod g2 { use std::ops::AddAssign; use ff::Field; - use group::CurveProjective; + use group::{CurveProjective, Group}; use pairing::bls12_381::*; fn bench_g2_mul_assign(c: &mut Criterion) { diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index aa2e2f0873..212e7baaf8 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -8,7 +8,7 @@ use criterion::{criterion_group, Criterion}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; -use group::CurveProjective; +use group::Group; use pairing::bls12_381::*; use pairing::{Engine, PairingCurveAffine}; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 8f0c993efa..757076fcf2 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -508,11 +508,7 @@ macro_rules! curve_impl { } } - impl CurveProjective for $projective { - type Scalar = $scalarfield; - type Base = $basefield; - type Affine = $affine; - + impl Group for $projective { fn random(rng: &mut R) -> Self { loop { let x = $basefield::random(rng); @@ -548,6 +544,14 @@ macro_rules! curve_impl { fn is_identity(&self) -> bool { self.z.is_zero() } + } + + impl PrimeGroup for $projective {} + + impl CurveProjective for $projective { + type Scalar = $scalarfield; + type Base = $basefield; + type Affine = $affine; fn is_normalized(&self) -> bool { self.is_identity() || self.z == $basefield::one() @@ -748,7 +752,9 @@ pub mod g1 { use super::g2::G2Affine; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; + use group::{ + CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1356,7 +1362,9 @@ pub mod g2 { use super::g1::G1Affine; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; + use group::{ + CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 734e6f3ccc..7bd819a69f 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; +use group::{CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError}; use super::*; use crate::*; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index f2277ba028..5769543893 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::{Endianness, Field, PrimeField}; -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CurveProjective, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; From 5397d73e0bb6bf4d9f8df32832f50abf10393206 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 20:03:29 +1200 Subject: [PATCH 028/210] group: Define group operations on Group trait The GroupOps trait represents the group operation (addition), and the combination of the group operation with group inversion (subtraction). Group inversion (negation) is constrained directly on the Group trait. --- group/src/lib.rs | 50 +++++++++++++++++++++++++++------------------- pairing/src/lib.rs | 10 +++++----- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/group/src/lib.rs b/group/src/lib.rs index 977f48654f..fc51af2ca6 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -12,9 +12,35 @@ pub mod tests; mod wnaf; pub use self::wnaf::Wnaf; +/// A helper trait for types with a group operation. +pub trait GroupOps: + Add + Sub + AddAssign + SubAssign +{ +} + +impl GroupOps for T where + T: Add + Sub + AddAssign + SubAssign +{ +} + +/// A helper trait for references with a group operation. +pub trait GroupOpsOwned: for<'r> GroupOps<&'r Rhs, Output> {} +impl GroupOpsOwned for T where T: for<'r> GroupOps<&'r Rhs, Output> {} + /// This trait represents an element of a cryptographic group. pub trait Group: - Clone + Copy + fmt::Debug + fmt::Display + Eq + Sized + Send + Sync + 'static + Clone + + Copy + + fmt::Debug + + fmt::Display + + Eq + + Sized + + Send + + Sync + + 'static + + Neg + + GroupOps + + GroupOpsOwned { /// Returns an element chosen uniformly at random using a user-provided RNG. fn random(rng: &mut R) -> Self; @@ -32,30 +58,12 @@ pub trait Group: /// This trait represents an element of a prime-order cryptographic group. pub trait PrimeGroup: Group {} -/// A helper trait for types implementing group addition. -pub trait CurveOps: - Add + Sub + AddAssign + SubAssign -{ -} - -impl CurveOps for T where - T: Add + Sub + AddAssign + SubAssign -{ -} - -/// A helper trait for references implementing group addition. -pub trait CurveOpsOwned: for<'r> CurveOps<&'r Rhs, Output> {} -impl CurveOpsOwned for T where T: for<'r> CurveOps<&'r Rhs, Output> {} - /// Projective representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. pub trait CurveProjective: Group - + Neg - + CurveOps - + CurveOpsOwned - + CurveOps<::Affine> - + CurveOpsOwned<::Affine> + + GroupOps<::Affine> + + GroupOpsOwned<::Affine> { type Scalar: PrimeField; type Base: Field; diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index e08eaacc88..341b0d0b76 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -21,7 +21,7 @@ pub mod tests; pub mod bls12_381; use ff::{Field, PrimeField, ScalarEngine}; -use group::{CurveAffine, CurveOps, CurveOpsOwned, CurveProjective}; +use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned}; use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -31,8 +31,8 @@ pub trait Engine: ScalarEngine { /// The projective representation of an element in G1. type G1: CurveProjective + From - + CurveOps - + CurveOpsOwned; + + GroupOps + + GroupOpsOwned; /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< @@ -46,8 +46,8 @@ pub trait Engine: ScalarEngine { /// The projective representation of an element in G2. type G2: CurveProjective + From - + CurveOps - + CurveOpsOwned; + + GroupOps + + GroupOpsOwned; /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< From e3d84280b9317eb820600f993792d7dc8115768d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 20:30:22 +1200 Subject: [PATCH 029/210] group: Sum + for<'a> Sum<&'a Self> bounds for Group --- bellman/src/groth16/tests/dummy_engine.rs | 13 +++++++++++++ group/src/lib.rs | 3 +++ pairing/src/bls12_381/ec.rs | 12 ++++++++++++ 3 files changed, 28 insertions(+) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 611be9752b..41ee7252a4 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -4,6 +4,7 @@ use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; use std::fmt; +use std::iter::Sum; use std::num::Wrapping; use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -47,6 +48,18 @@ impl ConditionallySelectable for Fr { } } +impl Sum for Fr { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), ::std::ops::Add::add) + } +} + +impl<'r> Sum<&'r Fr> for Fr { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), ::std::ops::Add::add) + } +} + impl Neg for Fr { type Output = Self; diff --git a/group/src/lib.rs b/group/src/lib.rs index fc51af2ca6..ab0e827440 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -5,6 +5,7 @@ use ff::{Field, PrimeField}; use rand::RngCore; use std::error::Error; use std::fmt; +use std::iter::Sum; use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; pub mod tests; @@ -38,6 +39,8 @@ pub trait Group: + Send + Sync + 'static + + Sum + + for<'a> Sum<&'a Self> + Neg + GroupOps + GroupOpsOwned diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 757076fcf2..e5982c6067 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -205,6 +205,18 @@ macro_rules! curve_impl { } } + impl ::std::iter::Sum for $projective { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + + impl<'r> ::std::iter::Sum<&'r $projective> for $projective { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + impl ::std::ops::Neg for $projective { type Output = Self; From d2aa87f0847ebc7c55712c078c6d00c3cd2548e4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 20:37:14 +1200 Subject: [PATCH 030/210] group: Move CurveProjective::double to Group --- bellman/src/groth16/tests/dummy_engine.rs | 8 +- group/src/lib.rs | 8 +- pairing/src/bls12_381/ec.rs | 96 +++++++++++------------ 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 41ee7252a4..27e504863d 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -381,6 +381,10 @@ impl Group for Fr { fn is_identity(&self) -> bool { ::is_zero(self) } + + fn double(&self) -> Self { + ::double(self) + } } impl PrimeGroup for Fr {} @@ -396,10 +400,6 @@ impl CurveProjective for Fr { true } - fn double(&self) -> Self { - ::double(self) - } - fn mul_assign::Repr>>(&mut self, other: S) { let tmp = Fr::from_repr(other.into()).unwrap(); diff --git a/group/src/lib.rs b/group/src/lib.rs index ab0e827440..3f1538c624 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -56,6 +56,10 @@ pub trait Group: /// Determines if this point is the identity. fn is_identity(&self) -> bool; + + /// Doubles this element. + #[must_use] + fn double(&self) -> Self; } /// This trait represents an element of a prime-order cryptographic group. @@ -80,10 +84,6 @@ pub trait CurveProjective: /// cheap affine conversion is possible. fn is_normalized(&self) -> bool; - /// Doubles this element. - #[must_use] - fn double(&self) -> Self; - /// Performs scalar multiplication of this element. fn mul_assign::Repr>>(&mut self, other: S); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index e5982c6067..178be3734d 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -556,6 +556,54 @@ macro_rules! curve_impl { fn is_identity(&self) -> bool { self.z.is_zero() } + + fn double(&self) -> Self { + if self.is_identity() { + return *self; + } + + // Other than the point at infinity, no points on E or E' + // can double to equal the point at infinity, as y=0 is + // never true for points on the curve. (-4 and -4u-4 + // are not cubic residue in their respective fields.) + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + // A = X1^2 + let a = self.x.square(); + + // B = Y1^2 + let b = self.y.square(); + + // C = B^2 + let c = b.square(); + + // D = 2*((X1+B)2-A-C) + let mut d = self.x; + d.add_assign(&b); + d = d.square(); + d.sub_assign(&a); + d.sub_assign(&c); + d = d.double(); + + // E = 3*A + let mut e = a.double(); + e.add_assign(&a); + + // F = E^2 + let f = e.square(); + + // Z3 = 2*Y1*Z1 + let z = self.z.double() * self.y; + + // X3 = F-2*D + let x = f - d.double(); + + // Y3 = E*(D-X3)-8*C + let y = e * (d - x) - c.double().double().double(); + + $projective { x, y, z } + } } impl PrimeGroup for $projective {} @@ -622,54 +670,6 @@ macro_rules! curve_impl { } } - fn double(&self) -> Self { - if self.is_identity() { - return *self; - } - - // Other than the point at infinity, no points on E or E' - // can double to equal the point at infinity, as y=0 is - // never true for points on the curve. (-4 and -4u-4 - // are not cubic residue in their respective fields.) - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - - // A = X1^2 - let a = self.x.square(); - - // B = Y1^2 - let b = self.y.square(); - - // C = B^2 - let c = b.square(); - - // D = 2*((X1+B)2-A-C) - let mut d = self.x; - d.add_assign(&b); - d = d.square(); - d.sub_assign(&a); - d.sub_assign(&c); - d = d.double(); - - // E = 3*A - let mut e = a.double(); - e.add_assign(&a); - - // F = E^2 - let f = e.square(); - - // Z3 = 2*Y1*Z1 - let z = self.z.double() * self.y; - - // X3 = F-2*D - let x = f - d.double(); - - // Y3 = E*(D-X3)-8*C - let y = e * (d - x) - c.double().double().double(); - - $projective { x, y, z } - } - fn mul_assign::Repr>>(&mut self, other: S) { let mut res = Self::identity(); From 0df950dc0d3211289ff11980e40c27ec18e1a450 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 22:53:39 +1200 Subject: [PATCH 031/210] group: Add Group::Subgroup associated type For prime-order groups, this may be Self. --- bellman/src/groth16/tests/dummy_engine.rs | 2 ++ group/src/lib.rs | 8 +++++++- pairing/src/bls12_381/ec.rs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 27e504863d..e7b6b17da0 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -366,6 +366,8 @@ impl Engine for DummyEngine { } impl Group for Fr { + type Subgroup = Fr; + fn random(rng: &mut R) -> Self { ::random(rng) } diff --git a/group/src/lib.rs b/group/src/lib.rs index 3f1538c624..8f711c44c5 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -44,7 +44,13 @@ pub trait Group: + Neg + GroupOps + GroupOpsOwned + + GroupOps<::Subgroup> + + GroupOpsOwned<::Subgroup> { + /// The large prime-order subgroup in which cryptographic operations are performed. + /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. + type Subgroup: PrimeGroup; + /// Returns an element chosen uniformly at random using a user-provided RNG. fn random(rng: &mut R) -> Self; @@ -52,7 +58,7 @@ pub trait Group: fn identity() -> Self; /// Returns a fixed generator of the prime-order subgroup. - fn generator() -> Self; + fn generator() -> Self::Subgroup; /// Determines if this point is the identity. fn is_identity(&self) -> bool; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 178be3734d..bb236832ae 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -521,6 +521,8 @@ macro_rules! curve_impl { } impl Group for $projective { + type Subgroup = Self; + fn random(rng: &mut R) -> Self { loop { let x = $basefield::random(rng); From ec8877825883a7b28dfaa13dbd31da2c69d1a99d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 23:16:07 +1200 Subject: [PATCH 032/210] group: Add scalar multiplication bounds to Group The Scalar associated type is moved from CurveProjective to Group. --- bellman/src/domain.rs | 2 +- bellman/src/groth16/prover.rs | 4 +- bellman/src/groth16/tests/dummy_engine.rs | 8 +-- group/src/lib.rs | 25 +++++++-- group/src/wnaf.rs | 9 +-- pairing/benches/bls12_381/ec.rs | 8 +-- pairing/src/bls12_381/ec.rs | 68 ++++++++++++++++------- pairing/src/lib.rs | 10 +++- pairing/src/tests/engine.rs | 12 ++-- 9 files changed, 90 insertions(+), 56 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 748a15a751..4e24f18b0b 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -221,7 +221,7 @@ impl> Group for Point Point(G::identity()) } fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by.to_repr()); + self.0.mul_assign(by); } fn group_add_assign(&mut self, other: &Self) { self.0.add_assign(&other.0); diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 97707fb09d..036dda8007 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -317,7 +317,7 @@ where let mut a_answer = a_inputs.wait()?; AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer); - a_answer.mul_assign(s); + MulAssign::::mul_assign(&mut a_answer, s); AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer); let mut b1_answer: E::G1 = b_g1_inputs.wait()?; @@ -326,7 +326,7 @@ where AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?); AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer); - b1_answer.mul_assign(r); + MulAssign::::mul_assign(&mut b1_answer, r); AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer); AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?); AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index e7b6b17da0..9823772270 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -367,6 +367,7 @@ impl Engine for DummyEngine { impl Group for Fr { type Subgroup = Fr; + type Scalar = Fr; fn random(rng: &mut R) -> Self { ::random(rng) @@ -394,7 +395,6 @@ impl PrimeGroup for Fr {} impl CurveProjective for Fr { type Affine = Fr; type Base = Fr; - type Scalar = Fr; fn batch_normalization(_: &mut [Self]) {} @@ -402,12 +402,6 @@ impl CurveProjective for Fr { true } - fn mul_assign::Repr>>(&mut self, other: S) { - let tmp = Fr::from_repr(other.into()).unwrap(); - - MulAssign::mul_assign(self, &tmp); - } - fn into_affine(&self) -> Fr { *self } diff --git a/group/src/lib.rs b/group/src/lib.rs index 8f711c44c5..b31cdedeb0 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -6,7 +6,7 @@ use rand::RngCore; use std::error::Error; use std::fmt; use std::iter::Sum; -use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; pub mod tests; @@ -28,6 +28,20 @@ impl GroupOps for T where pub trait GroupOpsOwned: for<'r> GroupOps<&'r Rhs, Output> {} impl GroupOpsOwned for T where T: for<'r> GroupOps<&'r Rhs, Output> {} +/// A helper trait for types implementing group scalar multiplication. +pub trait ScalarMul: Mul + MulAssign {} + +impl ScalarMul for T where T: Mul + MulAssign +{} + +/// A helper trait for references implementing group scalar multiplication. +/// +/// This trait, in combination with `ScalarMul`, is necessary to address type constraint +/// issues in `pairing::Engine` (specifically, to ensure that [`ff::ScalarEngine::Fr`] is +/// correctly constrained to implement these traits required by [`Group::Scalar`]). +pub trait ScalarMulOwned: for<'r> ScalarMul<&'r Rhs, Output> {} +impl ScalarMulOwned for T where T: for<'r> ScalarMul<&'r Rhs, Output> {} + /// This trait represents an element of a cryptographic group. pub trait Group: Clone @@ -46,11 +60,16 @@ pub trait Group: + GroupOpsOwned + GroupOps<::Subgroup> + GroupOpsOwned<::Subgroup> + + ScalarMul<::Scalar> + + ScalarMulOwned<::Scalar> { /// The large prime-order subgroup in which cryptographic operations are performed. /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. type Subgroup: PrimeGroup; + /// Scalars modulo the order of [`Group::Subgroup`]. + type Scalar: PrimeField; + /// Returns an element chosen uniformly at random using a user-provided RNG. fn random(rng: &mut R) -> Self; @@ -78,7 +97,6 @@ pub trait CurveProjective: + GroupOps<::Affine> + GroupOpsOwned<::Affine> { - type Scalar: PrimeField; type Base: Field; type Affine: CurveAffine; @@ -90,9 +108,6 @@ pub trait CurveProjective: /// cheap affine conversion is possible. fn is_normalized(&self) -> bool; - /// Performs scalar multiplication of this element. - fn mul_assign::Repr>>(&mut self, other: S); - /// Converts this element into its affine representation. fn into_affine(&self) -> Self::Affine; diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 79efd021ad..0ee46cafcb 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,7 +2,7 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::CurveProjective; +use super::{CurveProjective, Group}; /// Replaces the contents of `table` with a w-NAF window table for the given window size. pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { @@ -140,10 +140,7 @@ impl Wnaf<(), Vec, Vec> { /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform /// exponentiations with `.base(..)`. - pub fn scalar( - &mut self, - scalar: &::Scalar, - ) -> Wnaf, &[i64]> { + pub fn scalar(&mut self, scalar: &::Scalar) -> Wnaf, &[i64]> { // Compute the appropriate window size for the scalar. let window_size = G::recommended_wnaf_for_scalar(&scalar); @@ -198,7 +195,7 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs index d5ece12734..41c2b4cb10 100644 --- a/pairing/benches/bls12_381/ec.rs +++ b/pairing/benches/bls12_381/ec.rs @@ -5,7 +5,7 @@ pub(crate) mod g1 { use std::ops::AddAssign; use ff::Field; - use group::{CurveProjective, Group}; + use group::Group; use pairing::bls12_381::*; fn bench_g1_mul_assign(c: &mut Criterion) { @@ -24,7 +24,7 @@ pub(crate) mod g1 { c.bench_function("G1::mul_assign", |b| { b.iter(|| { let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); + tmp *= v[count].1; count = (count + 1) % SAMPLES; tmp }) @@ -92,7 +92,7 @@ pub(crate) mod g2 { use std::ops::AddAssign; use ff::Field; - use group::{CurveProjective, Group}; + use group::Group; use pairing::bls12_381::*; fn bench_g2_mul_assign(c: &mut Criterion) { @@ -111,7 +111,7 @@ pub(crate) mod g2 { c.bench_function("G2::mul_assign", |b| { b.iter(|| { let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); + tmp *= v[count].1; count = (count + 1) % SAMPLES; tmp }) diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index bb236832ae..9b88c36df7 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -520,8 +520,55 @@ macro_rules! curve_impl { } } + impl ::std::ops::Mul<<$projective as Group>::Scalar> for $projective { + type Output = Self; + + fn mul(mut self, other: <$projective as Group>::Scalar) -> Self { + self.mul_assign(&other); + self + } + } + + impl<'r> ::std::ops::Mul<&'r <$projective as Group>::Scalar> for $projective { + type Output = Self; + + fn mul(mut self, other: &'r <$projective as Group>::Scalar) -> Self { + self.mul_assign(other); + self + } + } + + impl ::std::ops::MulAssign<<$projective as Group>::Scalar> for $projective { + fn mul_assign(&mut self, other: <$projective as Group>::Scalar) { + self.mul_assign(&other); + } + } + + impl<'r> ::std::ops::MulAssign<&'r <$projective as Group>::Scalar> for $projective { + fn mul_assign(&mut self, other: &'r <$projective as Group>::Scalar) { + let mut res = Self::identity(); + + let mut found_one = false; + + for i in BitIterator::::new(other.to_repr()) { + if found_one { + res = res.double(); + } else { + found_one = i; + } + + if i { + res.add_assign(&*self); + } + } + + *self = res; + } + } + impl Group for $projective { type Subgroup = Self; + type Scalar = $scalarfield; fn random(rng: &mut R) -> Self { loop { @@ -611,7 +658,6 @@ macro_rules! curve_impl { impl PrimeGroup for $projective {} impl CurveProjective for $projective { - type Scalar = $scalarfield; type Base = $basefield; type Affine = $affine; @@ -672,26 +718,6 @@ macro_rules! curve_impl { } } - fn mul_assign::Repr>>(&mut self, other: S) { - let mut res = Self::identity(); - - let mut found_one = false; - - for i in BitIterator::::new(other.into()) { - if found_one { - res = res.double(); - } else { - found_one = i; - } - - if i { - res.add_assign(&*self); - } - } - - *self = res; - } - fn into_affine(&self) -> $affine { (*self).into() } diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 341b0d0b76..a89cc6a73c 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -21,7 +21,7 @@ pub mod tests; pub mod bls12_381; use ff::{Field, PrimeField, ScalarEngine}; -use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned}; +use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -32,7 +32,9 @@ pub trait Engine: ScalarEngine { type G1: CurveProjective + From + GroupOps - + GroupOpsOwned; + + GroupOpsOwned + + ScalarMul + + ScalarMulOwned; /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< @@ -47,7 +49,9 @@ pub trait Engine: ScalarEngine { type G2: CurveProjective + From + GroupOps - + GroupOpsOwned; + + GroupOpsOwned + + ScalarMul + + ScalarMulOwned; /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 5769543893..0a366e06b0 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -114,23 +114,21 @@ fn random_bilinearity_tests() { let d = E::Fr::random(&mut rng); let mut ac = a; - ac.mul_assign(c); + MulAssign::<&E::Fr>::mul_assign(&mut ac, &c); let mut ad = a; - ad.mul_assign(d); + MulAssign::<&E::Fr>::mul_assign(&mut ad, &d); let mut bc = b; - bc.mul_assign(c); + MulAssign::<&E::Fr>::mul_assign(&mut bc, &c); let mut bd = b; - bd.mul_assign(d); + MulAssign::<&E::Fr>::mul_assign(&mut bd, &d); let acbd = E::pairing(ac, bd); let adbc = E::pairing(ad, bc); - let mut cd = c; - cd.mul_assign(&d); - let mut cd = cd.to_repr(); + let mut cd = (c * &d).to_repr(); ::ReprEndianness::toggle_little_endian(&mut cd); use byteorder::ByteOrder; From 669f2b43eb30526ed64a8dbabd63b286382a220f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 14 May 2020 23:44:51 +1200 Subject: [PATCH 033/210] group: Return subtle::Choice from Group::is_identity --- bellman/src/groth16/generator.rs | 8 ++--- bellman/src/groth16/tests/dummy_engine.rs | 4 +-- group/Cargo.toml | 1 + group/src/lib.rs | 3 +- group/src/tests/mod.rs | 12 +++---- pairing/src/bls12_381/ec.rs | 44 +++++++++++------------ 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index b270ab3365..fe1fbdc65b 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -446,7 +446,7 @@ where // Don't allow any elements be unconstrained, so that // the L query is always fully dense. for e in l.iter() { - if e.is_identity() { + if e.is_identity().into() { return Err(SynthesisError::UnconstrainedVariable); } } @@ -472,19 +472,19 @@ where // Filter points at infinity away from A/B queries a: Arc::new( a.into_iter() - .filter(|e| !e.is_identity()) + .filter(|e| bool::from(!e.is_identity())) .map(|e| e.into_affine()) .collect(), ), b_g1: Arc::new( b_g1.into_iter() - .filter(|e| !e.is_identity()) + .filter(|e| bool::from(!e.is_identity())) .map(|e| e.into_affine()) .collect(), ), b_g2: Arc::new( b_g2.into_iter() - .filter(|e| !e.is_identity()) + .filter(|e| bool::from(!e.is_identity())) .map(|e| e.into_affine()) .collect(), ), diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 9823772270..4e824c60d6 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -381,8 +381,8 @@ impl Group for Fr { ::one() } - fn is_identity(&self) -> bool { - ::is_zero(self) + fn is_identity(&self) -> Choice { + Choice::from(if ::is_zero(self) { 1 } else { 0 }) } fn double(&self) -> Self { diff --git a/group/Cargo.toml b/group/Cargo.toml index b68c2fe6b9..ec5dff86a6 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -19,6 +19,7 @@ byteorder = { version = "1", default-features = false } ff = { version = "0.6", path = "../ff" } rand = "0.7" rand_xorshift = "0.2" +subtle = { version = "2.2.1", default-features = false } [badges] maintenance = { status = "actively-developed" } diff --git a/group/src/lib.rs b/group/src/lib.rs index b31cdedeb0..2d2e145085 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -7,6 +7,7 @@ use std::error::Error; use std::fmt; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::Choice; pub mod tests; @@ -80,7 +81,7 @@ pub trait Group: fn generator() -> Self::Subgroup; /// Determines if this point is the identity. - fn is_identity(&self) -> bool; + fn is_identity(&self) -> Choice; /// Doubles this element. #[must_use] diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index ef4a0e2695..9cb62898a4 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -14,13 +14,13 @@ pub fn curve_tests() { // Negation edge case with identity. { let z = G::identity().neg(); - assert!(z.is_identity()); + assert!(bool::from(z.is_identity())); } // Doubling edge case with identity. { let z = G::identity().double(); - assert!(z.is_identity()); + assert!(bool::from(z.is_identity())); } // Addition edge cases with identity @@ -34,9 +34,9 @@ pub fn curve_tests() { let mut z = G::identity(); z.add_assign(&G::identity()); - assert!(z.is_identity()); + assert!(bool::from(z.is_identity())); z.add_assign(&G::Affine::identity()); - assert!(z.is_identity()); + assert!(bool::from(z.is_identity())); let mut z2 = z; z2.add_assign(&r); @@ -208,11 +208,11 @@ fn random_negation_tests() { let mut t3 = t1; t3.add_assign(&t2); - assert!(t3.is_identity()); + assert!(bool::from(t3.is_identity())); let mut t4 = t1; t4.add_assign(&t2.into_affine()); - assert!(t4.is_identity()); + assert!(bool::from(t4.is_identity())); assert_eq!(t1.neg(), t2); } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 9b88c36df7..8728c4cf6b 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -42,11 +42,11 @@ macro_rules! curve_impl { impl PartialEq for $projective { fn eq(&self, other: &$projective) -> bool { - if self.is_identity() { - return other.is_identity(); + if self.is_identity().into() { + return other.is_identity().into(); } - if other.is_identity() { + if other.is_identity().into() { return false; } @@ -126,7 +126,7 @@ macro_rules! curve_impl { } fn is_on_curve(&self) -> bool { - if self.is_identity() { + if self.is_identity().into() { true } else { // Check that the point is on the curve @@ -141,7 +141,7 @@ macro_rules! curve_impl { } fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { - self.mul($scalarfield::char()).is_identity() + self.mul($scalarfield::char()).is_identity().into() } } @@ -151,7 +151,7 @@ macro_rules! curve_impl { #[inline] fn neg(self) -> Self { let mut ret = self; - if !ret.is_identity() { + if bool::from(!ret.is_identity()) { ret.y = ret.y.neg(); } ret @@ -223,7 +223,7 @@ macro_rules! curve_impl { #[inline] fn neg(self) -> Self { let mut ret = self; - if !ret.is_identity() { + if bool::from(!ret.is_identity()) { ret.y = ret.y.neg(); } ret @@ -252,12 +252,12 @@ macro_rules! curve_impl { impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective { fn add_assign(&mut self, other: &Self) { - if self.is_identity() { + if self.is_identity().into() { *self = *other; return; } - if other.is_identity() { + if other.is_identity().into() { return; } @@ -401,11 +401,11 @@ macro_rules! curve_impl { for $projective { fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { - if other.is_identity() { + if other.is_identity().into() { return; } - if self.is_identity() { + if self.is_identity().into() { self.x = other.x; self.y = other.y; self.z = $basefield::one(); @@ -579,7 +579,7 @@ macro_rules! curve_impl { if p.is_some().into() { let p = p.unwrap().scale_by_cofactor(); - if !p.is_identity() { + if bool::from(!p.is_identity()) { return p; } } @@ -602,12 +602,12 @@ macro_rules! curve_impl { // The point at infinity is always represented by // Z = 0. - fn is_identity(&self) -> bool { - self.z.is_zero() + fn is_identity(&self) -> Choice { + Choice::from(if self.z.is_zero() { 1 } else { 0 }) } fn double(&self) -> Self { - if self.is_identity() { + if self.is_identity().into() { return *self; } @@ -662,7 +662,7 @@ macro_rules! curve_impl { type Affine = $affine; fn is_normalized(&self) -> bool { - self.is_identity() || self.z == $basefield::one() + self.is_identity().into() || self.z == $basefield::one() } fn batch_normalization(v: &mut [Self]) { @@ -737,7 +737,7 @@ macro_rules! curve_impl { // coordinates with Z = 1. impl From<$affine> for $projective { fn from(p: $affine) -> $projective { - if p.is_identity() { + if p.is_identity().into() { $projective::identity() } else { $projective { @@ -753,7 +753,7 @@ macro_rules! curve_impl { // coordinates as X/Z^2, Y/Z^3. impl From<$projective> for $affine { fn from(p: $projective) -> $affine { - if p.is_identity() { + if p.is_identity().into() { $affine::identity() } else if p.z == $basefield::one() { // If Z is one, the point is already normalized. @@ -798,7 +798,7 @@ pub mod g1 { use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::CtOption; + use subtle::{Choice, CtOption}; curve_impl!( "G1", @@ -1114,7 +1114,7 @@ pub mod g1 { assert!(!p.is_in_correct_subgroup_assuming_on_curve()); let g1 = p.scale_by_cofactor(); - if !g1.is_identity() { + if bool::from(!g1.is_identity()) { assert_eq!(i, 4); let g1 = G1Affine::from(g1); @@ -1408,7 +1408,7 @@ pub mod g2 { use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::CtOption; + use subtle::{Choice, CtOption}; curve_impl!( "G2", @@ -1767,7 +1767,7 @@ pub mod g2 { assert!(!p.is_in_correct_subgroup_assuming_on_curve()); let g2 = p.scale_by_cofactor(); - if !g2.is_identity() { + if bool::from(!g2.is_identity()) { assert_eq!(i, 2); let g2 = G2Affine::from(g2); From b94d5670767458e65fd1334e69b220480c669159 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 15 May 2020 17:08:43 +1200 Subject: [PATCH 034/210] group: Direct-to-affine CurveProjective::batch_normalize Replaces the mutating CurveProjective::batch_normalization API, and removes the need for CurveProjective::is_normalized. The new temporary implementation in pairing::bls12_381::ec is adapted from bls12_381::g1. --- bellman/src/groth16/generator.rs | 71 +++++++++--------- bellman/src/groth16/tests/dummy_engine.rs | 8 +- group/src/lib.rs | 10 +-- group/src/tests/mod.rs | 17 +---- pairing/src/bls12_381/ec.rs | 89 ++++++++++------------- 5 files changed, 88 insertions(+), 107 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index fe1fbdc65b..21b48e064e 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -234,7 +234,7 @@ where let worker = Worker::new(); - let mut h = vec![E::G1::identity(); powers_of_tau.as_ref().len() - 1]; + let mut h = vec![E::G1Affine::identity(); powers_of_tau.as_ref().len() - 1]; { // Compute powers of tau { @@ -267,17 +267,20 @@ where scope.spawn(move |_scope| { // Set values of the H query to g1^{(tau^i * t(tau)) / delta} - for (h, p) in h.iter_mut().zip(p.iter()) { - // Compute final exponent - let mut exp = p.0; - exp.mul_assign(&coeff); - - // Exponentiate - *h = g1_wnaf.scalar(&exp); - } + let h_proj: Vec<_> = p[..h.len()] + .iter() + .map(|p| { + // Compute final exponent + let mut exp = p.0; + exp.mul_assign(&coeff); + + // Exponentiate + g1_wnaf.scalar(&exp) + }) + .collect(); // Batch normalize - E::G1::batch_normalization(h); + E::G1::batch_normalize(&h_proj, h); }); } }); @@ -287,11 +290,11 @@ where powers_of_tau.ifft(&worker); let powers_of_tau = powers_of_tau.into_coeffs(); - let mut a = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g1 = vec![E::G1::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g2 = vec![E::G2::identity(); assembly.num_inputs + assembly.num_aux]; - let mut ic = vec![E::G1::identity(); assembly.num_inputs]; - let mut l = vec![E::G1::identity(); assembly.num_aux]; + let mut a = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![E::G2Affine::identity(); assembly.num_inputs + assembly.num_aux]; + let mut ic = vec![E::G1Affine::identity(); assembly.num_inputs]; + let mut l = vec![E::G1Affine::identity(); assembly.num_aux]; fn eval( // wNAF window tables @@ -307,10 +310,10 @@ where ct: &[Vec<(E::Fr, usize)>], // Resulting evaluated QAP polynomials - a: &mut [E::G1], - b_g1: &mut [E::G1], - b_g2: &mut [E::G2], - ext: &mut [E::G1], + a: &mut [E::G1Affine], + b_g1: &mut [E::G1Affine], + b_g2: &mut [E::G2Affine], + ext: &mut [E::G1Affine], // Inverse coefficient for ext elements inv: &E::Fr, @@ -345,11 +348,16 @@ where let mut g2_wnaf = g2_wnaf.shared(); scope.spawn(move |_scope| { - for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a + let mut a_proj = vec![E::G1::identity(); a.len()]; + let mut b_g1_proj = vec![E::G1::identity(); b_g1.len()]; + let mut b_g2_proj = vec![E::G2::identity(); b_g2.len()]; + let mut ext_proj = vec![E::G1::identity(); ext.len()]; + + for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a_proj .iter_mut() - .zip(b_g1.iter_mut()) - .zip(b_g2.iter_mut()) - .zip(ext.iter_mut()) + .zip(b_g1_proj.iter_mut()) + .zip(b_g2_proj.iter_mut()) + .zip(ext_proj.iter_mut()) .zip(at.iter()) .zip(bt.iter()) .zip(ct.iter()) @@ -398,10 +406,10 @@ where } // Batch normalize - E::G1::batch_normalization(a); - E::G1::batch_normalization(b_g1); - E::G2::batch_normalization(b_g2); - E::G1::batch_normalization(ext); + E::G1::batch_normalize(&a_proj, a); + E::G1::batch_normalize(&b_g1_proj, b_g1); + E::G2::batch_normalize(&b_g2_proj, b_g2); + E::G1::batch_normalize(&ext_proj, ext); }); } }); @@ -461,31 +469,28 @@ where gamma_g2: g2.mul(gamma).into_affine(), delta_g1: g1.mul(delta).into_affine(), delta_g2: g2.mul(delta).into_affine(), - ic: ic.into_iter().map(|e| e.into_affine()).collect(), + ic, }; Ok(Parameters { vk, - h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), - l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + h: Arc::new(h), + l: Arc::new(l), // Filter points at infinity away from A/B queries a: Arc::new( a.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), b_g1: Arc::new( b_g1.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), b_g2: Arc::new( b_g2.into_iter() .filter(|e| bool::from(!e.is_identity())) - .map(|e| e.into_affine()) .collect(), ), }) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 4e824c60d6..222e8ba8cc 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -396,10 +396,12 @@ impl CurveProjective for Fr { type Affine = Fr; type Base = Fr; - fn batch_normalization(_: &mut [Self]) {} + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + assert_eq!(p.len(), q.len()); - fn is_normalized(&self) -> bool { - true + for (p, q) in p.iter().zip(q.iter_mut()) { + *q = p.into_affine(); + } } fn into_affine(&self) -> Fr { diff --git a/group/src/lib.rs b/group/src/lib.rs index 2d2e145085..eee06824e3 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -101,13 +101,9 @@ pub trait CurveProjective: type Base: Field; type Affine: CurveAffine; - /// Normalizes a slice of projective elements so that - /// conversion to affine is cheap. - fn batch_normalization(v: &mut [Self]); - - /// Checks if the point is already "normalized" so that - /// cheap affine conversion is possible. - fn is_normalized(&self) -> bool; + /// Converts a batch of projective elements into affine elements. This function will + /// panic if `p.len() != q.len()`. + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]); /// Converts this element into its affine representation. fn into_affine(&self) -> Self::Affine; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 9cb62898a4..ca5b508132 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -378,10 +378,6 @@ fn random_transformation_tests() { for _ in 0..10 { let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::>(); - for i in &v { - assert!(!i.is_normalized()); - } - use rand::distributions::{Distribution, Uniform}; let between = Uniform::new(0, 1000); // Sprinkle in some normalized points @@ -393,17 +389,12 @@ fn random_transformation_tests() { v[s] = v[s].into_affine().into_projective(); } - let expected_v = v - .iter() - .map(|v| v.into_affine().into_projective()) - .collect::>(); - G::batch_normalization(&mut v); + let expected_v = v.iter().map(|v| v.into_affine()).collect::>(); - for i in &v { - assert!(i.is_normalized()); - } + let mut normalized = vec![G::Affine::identity(); v.len()]; + G::batch_normalize(&v, &mut normalized); - assert_eq!(v, expected_v); + assert_eq!(normalized, expected_v); } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 8728c4cf6b..0df9a3eca7 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -661,60 +661,47 @@ macro_rules! curve_impl { type Base = $basefield; type Affine = $affine; - fn is_normalized(&self) -> bool { - self.is_identity().into() || self.z == $basefield::one() - } - - fn batch_normalization(v: &mut [Self]) { - // Montgomery’s Trick and Fast Implementation of Masked AES - // Genelle, Prouff and Quisquater - // Section 3.2 - - // First pass: compute [a, ab, abc, ...] - let mut prod = Vec::with_capacity(v.len()); - let mut tmp = $basefield::one(); - for g in v - .iter_mut() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - { - tmp.mul_assign(&g.z); - prod.push(tmp); + fn batch_normalize(p: &[Self], q: &mut [$affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = $basefield::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of $affine to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + if bool::from(!p.is_identity()) { + acc *= p.z; + } } - // Invert `tmp`. - tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero. - - // Second pass: iterate backwards to compute inverses - for (g, s) in v - .iter_mut() - // Backwards - .rev() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - // Backwards, skip last element, fill in one for last term. - .zip( - prod.into_iter() - .rev() - .skip(1) - .chain(Some($basefield::one())), - ) - { - // tmp := tmp * g.z; g.z := tmp * s = 1/z - let mut newtmp = tmp; - newtmp.mul_assign(&g.z); - g.z = tmp; - g.z.mul_assign(&s); - tmp = newtmp; - } + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); - // Perform affine transformations - for g in v.iter_mut().filter(|g| !g.is_normalized()) { - let mut z = g.z.square(); // 1/z^2 - g.x.mul_assign(&z); // x/z^2 - z.mul_assign(&g.z); // 1/z^3 - g.y.mul_assign(&z); // y/z^3 - g.z = $basefield::one(); // z = 1 + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + if bool::from(!skip) { + acc *= p.z; + } + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + if skip.into() { + *q = $affine::identity(); + } else { + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = false; + } } } From 0941dddc1311a31161c90d44befc9d02a61da5ef Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 15 May 2020 17:33:34 +1200 Subject: [PATCH 035/210] group: Return subtle::Choice from CurveAffine::is_identity --- bellman/src/groth16/mod.rs | 12 ++++++------ bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 4 ++-- bellman/src/multiexp.rs | 2 +- group/src/lib.rs | 2 +- pairing/src/bls12_381/ec.rs | 14 +++++++------- pairing/src/bls12_381/mod.rs | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index b338b0c62c..1c50bcb41a 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -54,7 +54,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -69,7 +69,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -84,7 +84,7 @@ impl Proof { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -198,7 +198,7 @@ impl VerifyingKey { .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -303,7 +303,7 @@ impl Parameters { } .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", @@ -325,7 +325,7 @@ impl Parameters { } .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .and_then(|e| { - if e.is_identity() { + if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, "point at infinity", diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 036dda8007..fbb30ee56a 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -295,7 +295,7 @@ where ); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); - if vk.delta_g1.is_identity() || vk.delta_g2.is_identity() { + if bool::from(vk.delta_g1.is_identity() | vk.delta_g2.is_identity()) { // If this element is zero, someone is trying to perform a // subversion-CRS attack. return Err(SynthesisError::UnexpectedIdentity); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 222e8ba8cc..be35b2ef0f 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -471,8 +471,8 @@ impl CurveAffine for Fr { ::one() } - fn is_identity(&self) -> bool { - ::is_zero(self) + fn is_identity(&self) -> Choice { + Choice::from(if ::is_zero(self) { 1 } else { 0 }) } fn mul::Repr>>(&self, other: S) -> Self::Projective { diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 643f743534..6cb660b24b 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -55,7 +55,7 @@ impl Source for (Arc>, usize) { .into()); } - if self.0[self.1].is_identity() { + if self.0[self.1].is_identity().into() { return Err(SynthesisError::UnexpectedIdentity); } diff --git a/group/src/lib.rs b/group/src/lib.rs index eee06824e3..d09498d76c 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -146,7 +146,7 @@ pub trait CurveAffine: /// Determines if this point represents the point at infinity; the /// additive identity. - fn is_identity(&self) -> bool; + fn is_identity(&self) -> Choice; /// Performs scalar multiplication of this element with mixed addition. fn mul::Repr>>(&self, other: S) -> Self::Projective; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 0df9a3eca7..0b95df59ed 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -177,8 +177,8 @@ macro_rules! curve_impl { Self::get_generator() } - fn is_identity(&self) -> bool { - self.infinity + fn is_identity(&self) -> Choice { + Choice::from(if self.infinity { 1 } else { 0 }) } fn mul::Repr>>(&self, by: S) -> $projective { @@ -893,7 +893,7 @@ pub mod g1 { fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); - if affine.is_identity() { + if affine.is_identity().into() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -990,7 +990,7 @@ pub mod g1 { fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); - if affine.is_identity() { + if affine.is_identity().into() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -1070,7 +1070,7 @@ pub mod g1 { impl G1Prepared { pub fn is_identity(&self) -> bool { - self.0.is_identity() + self.0.is_identity().into() } pub fn from_affine(p: G1Affine) -> Self { @@ -1515,7 +1515,7 @@ pub mod g2 { fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); - if affine.is_identity() { + if affine.is_identity().into() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; @@ -1629,7 +1629,7 @@ pub mod g2 { fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); - if affine.is_identity() { + if affine.is_identity().into() { // Set the second-most significant bit to indicate this point // is at infinity. res.0[0] |= 1 << 6; diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 748ba5f310..e6aaa063db 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -173,7 +173,7 @@ impl G2Prepared { } pub fn from_affine(q: G2Affine) -> Self { - if q.is_identity() { + if q.is_identity().into() { return G2Prepared { coeffs: vec![], infinity: true, From b0a3713d7e05fb36f49785175183f004e1d99e0f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 15 May 2020 17:49:45 +1200 Subject: [PATCH 036/210] group: Add mixed-addition scalar multiplication bounds to CurveAffine Replaces the explicit CurveAffine::mul trait method. --- bellman/src/groth16/generator.rs | 12 +++++------ bellman/src/groth16/prover.rs | 10 ++++----- bellman/src/groth16/tests/dummy_engine.rs | 9 -------- bellman/src/groth16/verifier.rs | 3 +-- bellman/src/multiexp.rs | 2 +- group/src/lib.rs | 9 ++++---- group/src/tests/mod.rs | 6 +++--- pairing/src/bls12_381/ec.rs | 26 +++++++++++++++++------ pairing/src/lib.rs | 9 ++++++-- 9 files changed, 48 insertions(+), 38 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 21b48e064e..11da63fcf5 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -463,12 +463,12 @@ where let g2 = g2.into_affine(); let vk = VerifyingKey:: { - alpha_g1: g1.mul(alpha).into_affine(), - beta_g1: g1.mul(beta).into_affine(), - beta_g2: g2.mul(beta).into_affine(), - gamma_g2: g2.mul(gamma).into_affine(), - delta_g1: g1.mul(delta).into_affine(), - delta_g2: g2.mul(delta).into_affine(), + alpha_g1: (g1 * &alpha).into_affine(), + beta_g1: (g1 * &beta).into_affine(), + beta_g2: (g2 * &beta).into_affine(), + gamma_g2: (g2 * &gamma).into_affine(), + delta_g1: (g1 * &delta).into_affine(), + delta_g2: (g2 * &delta).into_affine(), ic, }; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index fbb30ee56a..262bb75c4c 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -301,18 +301,18 @@ where return Err(SynthesisError::UnexpectedIdentity); } - let mut g_a = vk.delta_g1.mul(r); + let mut g_a = vk.delta_g1 * &r; AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1); - let mut g_b = vk.delta_g2.mul(s); + let mut g_b = vk.delta_g2 * &s; AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2); let mut g_c; { let mut rs = r; rs.mul_assign(&s); - g_c = vk.delta_g1.mul(rs); - AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.alpha_g1.mul(s)); - AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.beta_g1.mul(r)); + g_c = vk.delta_g1 * &rs; + AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.alpha_g1 * &s)); + AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.beta_g1 * &r)); } let mut a_answer = a_inputs.wait()?; AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index be35b2ef0f..cbfcb7eaa5 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -475,15 +475,6 @@ impl CurveAffine for Fr { Choice::from(if ::is_zero(self) { 1 } else { 0 }) } - fn mul::Repr>>(&self, other: S) -> Self::Projective { - let mut res = *self; - let tmp = Fr::from_repr(other.into()).unwrap(); - - MulAssign::mul_assign(&mut res, &tmp); - - res - } - fn into_projective(&self) -> Self::Projective { *self } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 0c8910151e..f80f143134 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,3 @@ -use ff::PrimeField; use group::{CurveAffine, CurveProjective}; use pairing::{Engine, PairingCurveAffine}; use std::ops::{AddAssign, Neg}; @@ -31,7 +30,7 @@ pub fn verify_proof<'a, E: Engine>( let mut acc = pvk.ic[0].into_projective(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { - AddAssign::<&E::G1>::add_assign(&mut acc, &b.mul(i.to_repr())); + AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i)); } // The original verification equation is: diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 6cb660b24b..40edb6b072 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -308,7 +308,7 @@ fn test_with_bls12() { let mut acc = G::identity(); for (base, exp) in bases.iter().zip(exponents.iter()) { - AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.to_repr())); + AddAssign::<&G>::add_assign(&mut acc, &(*base * *exp)); } acc diff --git a/group/src/lib.rs b/group/src/lib.rs index d09498d76c..adc47caf64 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -99,7 +99,9 @@ pub trait CurveProjective: + GroupOpsOwned<::Affine> { type Base: Field; - type Affine: CurveAffine; + type Affine: CurveAffine + + Mul + + for<'r> Mul; /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. @@ -131,6 +133,8 @@ pub trait CurveAffine: + Eq + 'static + Neg + + Mul<::Scalar, Output = ::Projective> + + for<'r> Mul<::Scalar, Output = ::Projective> { type Scalar: PrimeField; type Base: Field; @@ -148,9 +152,6 @@ pub trait CurveAffine: /// additive identity. fn is_identity(&self) -> Choice; - /// Performs scalar multiplication of this element with mixed addition. - fn mul::Repr>>(&self, other: S) -> Self::Projective; - /// Converts this element into its affine representation. fn into_projective(&self) -> Self::Projective; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index ca5b508132..36f2f261c3 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -1,7 +1,7 @@ use ff::{Field, PrimeField}; use rand::SeedableRng; use rand_xorshift::XorShiftRng; -use std::ops::Neg; +use std::ops::{Mul, Neg}; use crate::{CurveAffine, CurveProjective, EncodedPoint}; @@ -273,8 +273,8 @@ fn random_multiplication_tests() { tmp2.add_assign(&b); // Affine multiplication - let mut tmp3 = a_affine.mul(s); - tmp3.add_assign(&b_affine.mul(s)); + let mut tmp3 = Mul::::mul(a_affine, s); + tmp3.add_assign(Mul::::mul(b_affine, s)); assert_eq!(tmp1, tmp2); assert_eq!(tmp1, tmp3); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 0b95df59ed..0db08432c9 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -141,7 +141,8 @@ macro_rules! curve_impl { } fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { - self.mul($scalarfield::char()).is_identity().into() + let bits = BitIterator::::new($scalarfield::char()); + self.mul_bits_u8(bits).is_identity().into() } } @@ -158,6 +159,24 @@ macro_rules! curve_impl { } } + impl ::std::ops::Mul<$scalarfield> for $affine { + type Output = $projective; + + fn mul(self, by: $scalarfield) -> $projective { + let bits = BitIterator::::Repr>::new(by.into()); + self.mul_bits_u8(bits) + } + } + + impl<'r> ::std::ops::Mul<&'r $scalarfield> for $affine { + type Output = $projective; + + fn mul(self, by: &'r $scalarfield) -> $projective { + let bits = BitIterator::::Repr>::new(by.into()); + self.mul_bits_u8(bits) + } + } + impl CurveAffine for $affine { type Scalar = $scalarfield; type Base = $basefield; @@ -181,11 +200,6 @@ macro_rules! curve_impl { Choice::from(if self.infinity { 1 } else { 0 }) } - fn mul::Repr>>(&self, by: S) -> $projective { - let bits = BitIterator::::new(by.into()); - self.mul_bits_u8(bits) - } - fn into_projective(&self) -> $projective { (*self).into() } diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index a89cc6a73c..78394fdd4d 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -20,6 +20,7 @@ pub mod tests; pub mod bls12_381; +use core::ops::Mul; use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; use subtle::CtOption; @@ -43,7 +44,9 @@ pub trait Engine: ScalarEngine { Projective = Self::G1, Pair = Self::G2Affine, PairingResult = Self::Fqk, - > + From; + > + From + + Mul + + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; /// The projective representation of an element in G2. type G2: CurveProjective @@ -60,7 +63,9 @@ pub trait Engine: ScalarEngine { Projective = Self::G2, Pair = Self::G1Affine, PairingResult = Self::Fqk, - > + From; + > + From + + Mul + + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; /// The base field that hosts G1. type Fq: PrimeField; From b77f8ddddad1ba622329aeacfb35a8cfd0a898ea Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 19 May 2020 18:10:10 +1200 Subject: [PATCH 037/210] group: Remove EncodedPoint::{into_affine, into_affine_unchecked} Replaced by explicit APIs on the CurveAffine trait. GroupDecodingError has been moved into pairing::bls12_381::ec, as it is no longer used by the group traits. --- bellman/src/groth16/mod.rs | 182 ++++++++-------- bellman/src/groth16/tests/dummy_engine.rs | 26 ++- group/src/lib.rs | 78 ++----- group/src/tests/mod.rs | 18 +- pairing/src/bls12_381/ec.rs | 248 ++++++++++++++-------- pairing/src/bls12_381/tests/mod.rs | 134 +++++++----- 6 files changed, 382 insertions(+), 304 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 1c50bcb41a..8f5d7d4eea 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -46,29 +46,18 @@ impl Proof { } pub fn read(mut reader: R) -> io::Result { - let mut g1_repr = ::Compressed::empty(); - let mut g2_repr = ::Compressed::empty(); - - reader.read_exact(g1_repr.as_mut())?; - let a = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; + let read_g1 = |reader: &mut R| -> io::Result { + let mut g1_repr = ::Compressed::empty(); + reader.read_exact(g1_repr.as_mut())?; - reader.read_exact(g2_repr.as_mut())?; - let b = g2_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { + let affine = E::G1Affine::from_compressed(&g1_repr); + let affine = if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) + }; + + affine.and_then(|e| { if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, @@ -77,13 +66,21 @@ impl Proof { } else { Ok(e) } - })?; + }) + }; + + let read_g2 = |reader: &mut R| -> io::Result { + let mut g2_repr = ::Compressed::empty(); + reader.read_exact(g2_repr.as_mut())?; - reader.read_exact(g1_repr.as_mut())?; - let c = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { + let affine = E::G2Affine::from_compressed(&g2_repr); + let affine = if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) + }; + + affine.and_then(|e| { if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, @@ -92,7 +89,12 @@ impl Proof { } else { Ok(e) } - })?; + }) + }; + + let a = read_g1(&mut reader)?; + let b = read_g2(&mut reader)?; + let c = read_g1(&mut reader)?; Ok(Proof { a, b, c }) } @@ -155,58 +157,52 @@ impl VerifyingKey { } pub fn read(mut reader: R) -> io::Result { - let mut g1_repr = ::Uncompressed::empty(); - let mut g2_repr = ::Uncompressed::empty(); - - reader.read_exact(g1_repr.as_mut())?; - let alpha_g1 = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - reader.read_exact(g1_repr.as_mut())?; - let beta_g1 = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - reader.read_exact(g2_repr.as_mut())?; - let beta_g2 = g2_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - reader.read_exact(g2_repr.as_mut())?; - let gamma_g2 = g2_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - reader.read_exact(g1_repr.as_mut())?; - let delta_g1 = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - reader.read_exact(g2_repr.as_mut())?; - let delta_g2 = g2_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let read_g1 = |reader: &mut R| -> io::Result { + let mut g1_repr = ::Uncompressed::empty(); + reader.read_exact(g1_repr.as_mut())?; + + let affine = E::G1Affine::from_uncompressed(&g1_repr); + if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) + } + }; + + let read_g2 = |reader: &mut R| -> io::Result { + let mut g2_repr = ::Uncompressed::empty(); + reader.read_exact(g2_repr.as_mut())?; + + let affine = E::G2Affine::from_uncompressed(&g2_repr); + if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) + } + }; + + let alpha_g1 = read_g1(&mut reader)?; + let beta_g1 = read_g1(&mut reader)?; + let beta_g2 = read_g2(&mut reader)?; + let gamma_g2 = read_g2(&mut reader)?; + let delta_g1 = read_g1(&mut reader)?; + let delta_g2 = read_g2(&mut reader)?; let ic_len = reader.read_u32::()? as usize; let mut ic = vec![]; for _ in 0..ic_len { - reader.read_exact(g1_repr.as_mut())?; - let g1 = g1_repr - .into_affine() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; + let g1 = read_g1(&mut reader).and_then(|e| { + if e.is_identity().into() { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "point at infinity", + )) + } else { + Ok(e) + } + })?; ic.push(g1); } @@ -296,13 +292,19 @@ impl Parameters { let mut repr = ::Uncompressed::empty(); reader.read_exact(repr.as_mut())?; - if checked { - repr.into_affine() + let affine = if checked { + E::G1Affine::from_uncompressed(&repr) } else { - repr.into_affine_unchecked() - } - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { + E::G1Affine::from_uncompressed_unchecked(&repr) + }; + + let affine = if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) + }; + + affine.and_then(|e| { if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, @@ -318,13 +320,19 @@ impl Parameters { let mut repr = ::Uncompressed::empty(); reader.read_exact(repr.as_mut())?; - if checked { - repr.into_affine() + let affine = if checked { + E::G2Affine::from_uncompressed(&repr) } else { - repr.into_affine_unchecked() - } - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - .and_then(|e| { + E::G2Affine::from_uncompressed_unchecked(&repr) + }; + + let affine = if affine.is_some().into() { + Ok(affine.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) + }; + + affine.and_then(|e| { if e.is_identity().into() { Err(io::Error::new( io::ErrorKind::InvalidData, diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index cbfcb7eaa5..b85c65f70e 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField, ScalarEngine}; -use group::{CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup}; +use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; @@ -443,14 +443,6 @@ impl EncodedPoint for FakePoint { unimplemented!() } - fn into_affine(&self) -> Result { - unimplemented!() - } - - fn into_affine_unchecked(&self) -> Result { - unimplemented!() - } - fn from_affine(_: Self::Affine) -> Self { unimplemented!() } @@ -478,6 +470,22 @@ impl CurveAffine for Fr { fn into_projective(&self) -> Self::Projective { *self } + + fn from_compressed(_bytes: &Self::Compressed) -> CtOption { + unimplemented!() + } + + fn from_compressed_unchecked(_bytes: &Self::Compressed) -> CtOption { + unimplemented!() + } + + fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { + unimplemented!() + } + + fn from_uncompressed_unchecked(_bytes: &Self::Uncompressed) -> CtOption { + unimplemented!() + } } impl PairingCurveAffine for Fr { diff --git a/group/src/lib.rs b/group/src/lib.rs index adc47caf64..dac6f474e2 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -3,11 +3,10 @@ use ff::{Field, PrimeField}; use rand::RngCore; -use std::error::Error; use std::fmt; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::Choice; +use subtle::{Choice, CtOption}; pub mod tests; @@ -155,12 +154,34 @@ pub trait CurveAffine: /// Converts this element into its affine representation. fn into_projective(&self) -> Self::Projective; + /// Attempts to deserialize an element from its compressed encoding. + fn from_compressed(bytes: &Self::Compressed) -> CtOption; + + /// Attempts to deserialize a compressed element, not checking if the element is in + /// the correct subgroup. + /// + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using + /// [`CurveAffine::from_compressed`] instead. + fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption; + /// Converts this element into its compressed encoding, so long as it's not /// the point at infinity. fn into_compressed(&self) -> Self::Compressed { ::from_affine(*self) } + /// Attempts to deserialize an element from its uncompressed encoding. + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; + + /// Attempts to deserialize an uncompressed element, not checking if the element is in + /// the correct subgroup. + /// + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using + /// [`CurveAffine::from_uncompressed`] instead. + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption; + /// Converts this element into its uncompressed encoding, so long as it's not /// the point at infinity. fn into_uncompressed(&self) -> Self::Uncompressed { @@ -180,60 +201,7 @@ pub trait EncodedPoint: /// Returns the number of bytes consumed by this representation. fn size() -> usize; - /// Converts an `EncodedPoint` into a `CurveAffine` element, - /// if the encoding represents a valid element. - fn into_affine(&self) -> Result; - - /// Converts an `EncodedPoint` into a `CurveAffine` element, - /// without guaranteeing that the encoding represents a valid - /// element. This is useful when the caller knows the encoding is - /// valid already. - /// - /// If the encoding is invalid, this can break API invariants, - /// so caution is strongly encouraged. - fn into_affine_unchecked(&self) -> Result; - /// Creates an `EncodedPoint` from an affine point, as long as the /// point is not the point at infinity. fn from_affine(affine: Self::Affine) -> Self; } - -/// An error that may occur when trying to decode an `EncodedPoint`. -#[derive(Debug)] -pub enum GroupDecodingError { - /// The coordinate(s) do not lie on the curve. - NotOnCurve, - /// The element is not part of the r-order subgroup. - NotInSubgroup, - /// One of the coordinates could not be decoded - CoordinateDecodingError(&'static str), - /// The compression mode of the encoded element was not as expected - UnexpectedCompressionMode, - /// The encoding contained bits that should not have been set - UnexpectedInformation, -} - -impl Error for GroupDecodingError { - fn description(&self) -> &str { - match *self { - GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve", - GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup", - GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded", - GroupDecodingError::UnexpectedCompressionMode => { - "encoding has unexpected compression mode" - } - GroupDecodingError::UnexpectedInformation => "encoding has unexpected information", - } - } -} - -impl fmt::Display for GroupDecodingError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - GroupDecodingError::CoordinateDecodingError(description) => { - write!(f, "{} decoding error", description) - } - _ => write!(f, "{}", self.description()), - } - } -} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 36f2f261c3..53fb483810 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,7 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective, EncodedPoint}; +use crate::{CurveAffine, CurveProjective}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ @@ -405,18 +405,12 @@ fn random_encoding_tests() { ]); assert_eq!( - G::Affine::identity() - .into_uncompressed() - .into_affine() - .unwrap(), + G::Affine::from_uncompressed(&G::Affine::identity().into_uncompressed()).unwrap(), G::Affine::identity() ); assert_eq!( - G::Affine::identity() - .into_compressed() - .into_affine() - .unwrap(), + G::Affine::from_compressed(&G::Affine::identity().into_compressed()).unwrap(), G::Affine::identity() ); @@ -424,17 +418,17 @@ fn random_encoding_tests() { let mut r = G::random(&mut rng).into_affine(); let uncompressed = r.into_uncompressed(); - let de_uncompressed = uncompressed.into_affine().unwrap(); + let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); assert_eq!(de_uncompressed, r); let compressed = r.into_compressed(); - let de_compressed = compressed.into_affine().unwrap(); + let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); r = r.neg(); let compressed = r.into_compressed(); - let de_compressed = compressed.into_affine().unwrap(); + let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 0db08432c9..82b080e401 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -17,6 +17,12 @@ macro_rules! curve_impl { pub(crate) infinity: bool, } + impl Default for $affine { + fn default() -> Self { + Self::identity() + } + } + impl ::std::fmt::Display for $affine { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { if self.infinity { @@ -27,6 +33,21 @@ macro_rules! curve_impl { } } + impl ::subtle::ConditionallySelectable for $affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $affine { + x: $basefield::conditional_select(&a.x, &b.x, choice), + y: $basefield::conditional_select(&a.y, &b.y, choice), + // Obviously not constant-time, but this code will be replaced. + infinity: if choice.into() { + b.infinity + } else { + a.infinity + }, + } + } + } + #[derive(Copy, Clone, Debug, Eq)] pub struct $projective { pub(crate) x: $basefield, @@ -203,6 +224,53 @@ macro_rules! curve_impl { fn into_projective(&self) -> $projective { (*self).into() } + + fn from_compressed(bytes: &Self::Compressed) -> CtOption { + Self::from_compressed_unchecked(bytes).and_then(|affine| { + // NB: Decompression guarantees that it is on the curve already. + CtOption::new( + affine, + Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }), + ) + }) + } + + fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new(p, Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked(bytes).and_then(|affine| { + CtOption::new( + affine, + Choice::from( + if affine.is_on_curve() + && affine.is_in_correct_subgroup_assuming_on_curve() + { + 1 + } else { + 0 + }, + ), + ) + }) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new(p, Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } } impl PairingCurveAffine for $affine { @@ -788,14 +856,52 @@ macro_rules! curve_impl { }; } +use std::error::Error; +use std::fmt; + +/// An error that may occur when trying to decode an `EncodedPoint`. +#[derive(Debug)] +enum GroupDecodingError { + /// The coordinate(s) do not lie on the curve. + NotOnCurve, + /// One of the coordinates could not be decoded + CoordinateDecodingError(&'static str), + /// The compression mode of the encoded element was not as expected + UnexpectedCompressionMode, + /// The encoding contained bits that should not have been set + UnexpectedInformation, +} + +impl Error for GroupDecodingError { + fn description(&self) -> &str { + match *self { + GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve", + GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded", + GroupDecodingError::UnexpectedCompressionMode => { + "encoding has unexpected compression mode" + } + GroupDecodingError::UnexpectedInformation => "encoding has unexpected information", + } + } +} + +impl fmt::Display for GroupDecodingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + GroupDecodingError::CoordinateDecodingError(description) => { + write!(f, "{} decoding error", description) + } + _ => write!(f, "{}", self.description()), + } + } +} + pub mod g1 { use super::super::{Fq, Fq12, FqRepr, Fr}; - use super::g2::G2Affine; + use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{ - CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup, - }; + use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -834,26 +940,7 @@ pub mod g1 { } } - impl EncodedPoint for G1Uncompressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Uncompressed([0; 96]) - } - fn size() -> usize { - 96 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - if !affine.is_on_curve() { - Err(GroupDecodingError::NotOnCurve) - } else if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } + impl G1Uncompressed { fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -904,6 +991,17 @@ pub mod g1 { }) } } + } + + impl EncodedPoint for G1Uncompressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Uncompressed([0; 96]) + } + fn size() -> usize { + 96 + } fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); @@ -941,26 +1039,7 @@ pub mod g1 { } } - impl EncodedPoint for G1Compressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Compressed([0; 48]) - } - fn size() -> usize { - 48 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - // NB: Decompression guarantees that it is on the curve already. - - if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } + impl G1Compressed { fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1001,6 +1080,17 @@ pub mod g1 { } } } + } + + impl EncodedPoint for G1Compressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Compressed([0; 48]) + } + fn size() -> usize { + 48 + } fn from_affine(affine: G1Affine) -> Self { let mut res = Self::empty(); @@ -1400,12 +1490,10 @@ pub mod g1 { pub mod g2 { use super::super::{Fq, Fq12, Fq2, FqRepr, Fr}; - use super::g1::G1Affine; + use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{ - CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError, PrimeGroup, - }; + use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1444,26 +1532,7 @@ pub mod g2 { } } - impl EncodedPoint for G2Uncompressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Uncompressed([0; 192]) - } - fn size() -> usize { - 192 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - if !affine.is_on_curve() { - Err(GroupDecodingError::NotOnCurve) - } else if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } + impl G2Uncompressed { fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1526,6 +1595,17 @@ pub mod g2 { }) } } + } + + impl EncodedPoint for G2Uncompressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Uncompressed([0; 192]) + } + fn size() -> usize { + 192 + } fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); @@ -1565,26 +1645,7 @@ pub mod g2 { } } - impl EncodedPoint for G2Compressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Compressed([0; 96]) - } - fn size() -> usize { - 96 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - // NB: Decompression guarantees that it is on the curve already. - - if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } + impl G2Compressed { fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1640,6 +1701,17 @@ pub mod g2 { } } } + } + + impl EncodedPoint for G2Compressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Compressed([0; 96]) + } + fn size() -> usize { + 96 + } fn from_affine(affine: G2Affine) -> Self { let mut res = Self::empty(); diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 7bd819a69f..815c2bb4f5 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, EncodedPoint, Group, GroupDecodingError}; +use group::{CurveAffine, CurveProjective, EncodedPoint, Group}; use super::*; use crate::*; @@ -55,7 +55,7 @@ fn test_pairing_result_against_relic() { }); } -fn test_vectors>(expected: &[u8]) { +fn uncompressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); let mut v = vec![]; @@ -63,13 +63,41 @@ fn test_vectors>(expecte let mut expected = expected; for _ in 0..1000 { let e_affine = e.into_affine(); - let encoded = E::from_affine(e_affine); + let encoded = ::Uncompressed::from_affine(e_affine); v.extend_from_slice(encoded.as_ref()); - let mut decoded = E::empty(); - decoded.as_mut().copy_from_slice(&expected[0..E::size()]); - expected = &expected[E::size()..]; - let decoded = decoded.into_affine().unwrap(); + let mut decoded = ::Uncompressed::empty(); + decoded + .as_mut() + .copy_from_slice(&expected[0..::Uncompressed::size()]); + expected = &expected[::Uncompressed::size()..]; + let decoded = G::Affine::from_uncompressed(&decoded).unwrap(); + assert_eq!(e_affine, decoded); + + e.add_assign(&G::generator()); + } + } + + assert_eq!(&v[..], expected); +} + +fn compressed_test_vectors(expected: &[u8]) { + let mut e = G::identity(); + + let mut v = vec![]; + { + let mut expected = expected; + for _ in 0..1000 { + let e_affine = e.into_affine(); + let encoded = ::Compressed::from_affine(e_affine); + v.extend_from_slice(encoded.as_ref()); + + let mut decoded = ::Compressed::empty(); + decoded + .as_mut() + .copy_from_slice(&expected[0..::Compressed::size()]); + expected = &expected[::Compressed::size()..]; + let decoded = G::Affine::from_compressed(&decoded).unwrap(); assert_eq!(e_affine, decoded); e.add_assign(&G::generator()); @@ -81,22 +109,22 @@ fn test_vectors>(expecte #[test] fn test_g1_uncompressed_valid_vectors() { - test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); + uncompressed_test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); } #[test] fn test_g1_compressed_valid_vectors() { - test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); + compressed_test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); } #[test] fn test_g2_uncompressed_valid_vectors() { - test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); + uncompressed_test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); } #[test] fn test_g2_compressed_valid_vectors() { - test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); + compressed_test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); } #[test] @@ -107,7 +135,7 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + if G1Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected an uncompressed point"); @@ -117,7 +145,7 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G1Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -127,7 +155,7 @@ fn test_g1_uncompressed_invalid_vectors() { for i in 0..G1Uncompressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G1Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -140,7 +168,7 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut o = o; o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + if G1Affine::from_uncompressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected an uncompressed point"); @@ -153,8 +181,8 @@ fn test_g1_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[..48].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate"); + if G1Affine::from_uncompressed(&o).is_none().into() { + // x coordinate } else { panic!("should have rejected the point") } @@ -164,8 +192,8 @@ fn test_g1_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[48..].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "y coordinate"); + if G1Affine::from_uncompressed(&o).is_none().into() { + // y coordinate } else { panic!("should have rejected the point") } @@ -177,7 +205,7 @@ fn test_g1_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[..48].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + if G1Affine::from_uncompressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because it isn't on the curve") @@ -201,7 +229,7 @@ fn test_g1_uncompressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(x.to_repr().as_ref()); o.as_mut()[48..].copy_from_slice(y.to_repr().as_ref()); - if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + if G1Affine::from_uncompressed(&o).is_none().into() { break; } else { panic!( @@ -223,7 +251,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + if G2Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected an uncompressed point"); @@ -233,7 +261,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G2Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -243,7 +271,7 @@ fn test_g2_uncompressed_invalid_vectors() { for i in 0..G2Uncompressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G2Affine::from_uncompressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -256,7 +284,7 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + if G2Affine::from_uncompressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected an uncompressed point"); @@ -269,8 +297,8 @@ fn test_g2_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[..48].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate (c1)"); + if G2Affine::from_uncompressed(&o).is_none().into() { + // x coordinate (c1) } else { panic!("should have rejected the point") } @@ -280,8 +308,8 @@ fn test_g2_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[48..96].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate (c0)"); + if G2Affine::from_uncompressed(&o).is_none().into() { + // x coordinate (c0) } else { panic!("should have rejected the point") } @@ -291,8 +319,8 @@ fn test_g2_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[96..144].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "y coordinate (c1)"); + if G2Affine::from_uncompressed(&o).is_none().into() { + // y coordinate (c1) } else { panic!("should have rejected the point") } @@ -302,8 +330,8 @@ fn test_g2_uncompressed_invalid_vectors() { let mut o = o; o.as_mut()[144..].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "y coordinate (c0)"); + if G2Affine::from_uncompressed(&o).is_none().into() { + // y coordinate (c0) } else { panic!("should have rejected the point") } @@ -316,7 +344,7 @@ fn test_g2_uncompressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[48..96].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + if G2Affine::from_uncompressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because it isn't on the curve") @@ -345,7 +373,7 @@ fn test_g2_uncompressed_invalid_vectors() { o.as_mut()[96..144].copy_from_slice(y.c1.to_repr().as_ref()); o.as_mut()[144..].copy_from_slice(y.c0.to_repr().as_ref()); - if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + if G2Affine::from_uncompressed(&o).is_none().into() { break; } else { panic!( @@ -367,7 +395,7 @@ fn test_g1_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + if G1Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -377,7 +405,7 @@ fn test_g1_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G1Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -387,7 +415,7 @@ fn test_g1_compressed_invalid_vectors() { for i in 0..G1Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G1Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -400,7 +428,7 @@ fn test_g1_compressed_invalid_vectors() { { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + if G1Affine::from_compressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -414,8 +442,8 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate"); + if G1Affine::from_compressed(&o).is_none().into() { + // x coordinate } else { panic!("should have rejected the point") } @@ -436,7 +464,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + if G1Affine::from_compressed(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -459,7 +487,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + if G1Affine::from_compressed(&o).is_none().into() { break; } else { panic!( @@ -481,7 +509,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + if G2Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -491,7 +519,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G2Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -501,7 +529,7 @@ fn test_g2_compressed_invalid_vectors() { for i in 0..G2Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + if G2Affine::from_compressed(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -514,7 +542,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + if G2Affine::from_compressed(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -528,8 +556,8 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate (c1)"); + if G2Affine::from_compressed(&o).is_none().into() { + // x coordinate (c1) } else { panic!("should have rejected the point") } @@ -540,8 +568,8 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..96].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { - assert_eq!(coordinate, "x coordinate (c0)"); + if G2Affine::from_compressed(&o).is_none().into() { + // x coordinate (c0) } else { panic!("should have rejected the point") } @@ -569,7 +597,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + if G2Affine::from_compressed(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -599,7 +627,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + if G2Affine::from_compressed(&o).is_none().into() { break; } else { panic!( From ceecd32ac431d37538a41ec95093aecf2a1799b9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 19 May 2020 18:30:31 +1200 Subject: [PATCH 038/210] group: Remove EncodedPoint::from_affine The EncodedPoint trait is replaced by explicit bounds on the CurveAffine::{Uncompressed, Compressed} associated types. --- bellman/src/groth16/mod.rs | 14 +-- bellman/src/groth16/tests/dummy_engine.rs | 28 +++--- group/src/lib.rs | 29 +----- pairing/src/bls12_381/ec.rs | 104 +++++++++++----------- pairing/src/bls12_381/tests/mod.rs | 28 +++--- 5 files changed, 90 insertions(+), 113 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 8f5d7d4eea..42b96d90e2 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{CurveAffine, EncodedPoint}; +use group::CurveAffine; use pairing::{Engine, PairingCurveAffine}; use crate::SynthesisError; @@ -47,7 +47,7 @@ impl Proof { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Compressed::empty(); + let mut g1_repr = ::Compressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_compressed(&g1_repr); @@ -70,7 +70,7 @@ impl Proof { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Compressed::empty(); + let mut g2_repr = ::Compressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_compressed(&g2_repr); @@ -158,7 +158,7 @@ impl VerifyingKey { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Uncompressed::empty(); + let mut g1_repr = ::Uncompressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_uncompressed(&g1_repr); @@ -170,7 +170,7 @@ impl VerifyingKey { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Uncompressed::empty(); + let mut g2_repr = ::Uncompressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_uncompressed(&g2_repr); @@ -289,7 +289,7 @@ impl Parameters { pub fn read(mut reader: R, checked: bool) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::empty(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { @@ -317,7 +317,7 @@ impl Parameters { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::empty(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index b85c65f70e..c00d42a9a2 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField, ScalarEngine}; -use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; +use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; @@ -417,7 +417,7 @@ impl CurveProjective for Fr { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct FakePoint; impl AsMut<[u8]> for FakePoint { @@ -432,22 +432,6 @@ impl AsRef<[u8]> for FakePoint { } } -impl EncodedPoint for FakePoint { - type Affine = Fr; - - fn empty() -> Self { - unimplemented!() - } - - fn size() -> usize { - unimplemented!() - } - - fn from_affine(_: Self::Affine) -> Self { - unimplemented!() - } -} - impl CurveAffine for Fr { type Compressed = FakePoint; type Uncompressed = FakePoint; @@ -479,6 +463,10 @@ impl CurveAffine for Fr { unimplemented!() } + fn into_compressed(&self) -> Self::Compressed { + unimplemented!() + } + fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { unimplemented!() } @@ -486,6 +474,10 @@ impl CurveAffine for Fr { fn from_uncompressed_unchecked(_bytes: &Self::Uncompressed) -> CtOption { unimplemented!() } + + fn into_uncompressed(&self) -> Self::Uncompressed { + unimplemented!() + } } impl PairingCurveAffine for Fr { diff --git a/group/src/lib.rs b/group/src/lib.rs index dac6f474e2..13a17c8e3f 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -138,8 +138,8 @@ pub trait CurveAffine: type Scalar: PrimeField; type Base: Field; type Projective: CurveProjective; - type Uncompressed: EncodedPoint; - type Compressed: EncodedPoint; + type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; + type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Returns the additive identity. fn identity() -> Self; @@ -167,9 +167,7 @@ pub trait CurveAffine: /// Converts this element into its compressed encoding, so long as it's not /// the point at infinity. - fn into_compressed(&self) -> Self::Compressed { - ::from_affine(*self) - } + fn into_compressed(&self) -> Self::Compressed; /// Attempts to deserialize an element from its uncompressed encoding. fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; @@ -184,24 +182,5 @@ pub trait CurveAffine: /// Converts this element into its uncompressed encoding, so long as it's not /// the point at infinity. - fn into_uncompressed(&self) -> Self::Uncompressed { - ::from_affine(*self) - } -} - -/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`. -pub trait EncodedPoint: - Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static -{ - type Affine: CurveAffine; - - /// Creates an empty representation. - fn empty() -> Self; - - /// Returns the number of bytes consumed by this representation. - fn size() -> usize; - - /// Creates an `EncodedPoint` from an affine point, as long as the - /// point is not the point at infinity. - fn from_affine(affine: Self::Affine) -> Self; + fn into_uncompressed(&self) -> Self::Uncompressed; } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 82b080e401..5225535d71 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -247,6 +247,10 @@ macro_rules! curve_impl { } } + fn into_compressed(&self) -> Self::Compressed { + $compressed::from_affine(*self) + } + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { Self::from_uncompressed_unchecked(bytes).and_then(|affine| { CtOption::new( @@ -271,6 +275,10 @@ macro_rules! curve_impl { CtOption::new(Self::identity(), Choice::from(0)) } } + + fn into_uncompressed(&self) -> Self::Uncompressed { + $uncompressed::from_affine(*self) + } } impl PairingCurveAffine for $affine { @@ -901,7 +909,7 @@ pub mod g1 { use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -922,6 +930,12 @@ pub mod g1 { #[derive(Copy, Clone)] pub struct G1Uncompressed([u8; 96]); + impl Default for G1Uncompressed { + fn default() -> Self { + G1Uncompressed([0; 96]) + } + } + impl AsRef<[u8]> for G1Uncompressed { fn as_ref(&self) -> &[u8] { &self.0 @@ -941,6 +955,10 @@ pub mod g1 { } impl G1Uncompressed { + #[cfg(test)] + pub(crate) fn size() -> usize { + 96 + } fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -991,19 +1009,8 @@ pub mod g1 { }) } } - } - - impl EncodedPoint for G1Uncompressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Uncompressed([0; 96]) - } - fn size() -> usize { - 96 - } fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::empty(); + let mut res = Self::default(); if affine.is_identity().into() { // Set the second-most significant bit to indicate this point @@ -1021,6 +1028,12 @@ pub mod g1 { #[derive(Copy, Clone)] pub struct G1Compressed([u8; 48]); + impl Default for G1Compressed { + fn default() -> Self { + G1Compressed([0; 48]) + } + } + impl AsRef<[u8]> for G1Compressed { fn as_ref(&self) -> &[u8] { &self.0 @@ -1040,6 +1053,10 @@ pub mod g1 { } impl G1Compressed { + #[cfg(test)] + pub(crate) fn size() -> usize { + 48 + } fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1080,19 +1097,8 @@ pub mod g1 { } } } - } - - impl EncodedPoint for G1Compressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Compressed([0; 48]) - } - fn size() -> usize { - 48 - } fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::empty(); + let mut res = Self::default(); if affine.is_identity().into() { // Set the second-most significant bit to indicate this point @@ -1493,7 +1499,7 @@ pub mod g2 { use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1514,6 +1520,12 @@ pub mod g2 { #[derive(Copy, Clone)] pub struct G2Uncompressed([u8; 192]); + impl Default for G2Uncompressed { + fn default() -> Self { + G2Uncompressed([0; 192]) + } + } + impl AsRef<[u8]> for G2Uncompressed { fn as_ref(&self) -> &[u8] { &self.0 @@ -1533,6 +1545,10 @@ pub mod g2 { } impl G2Uncompressed { + #[cfg(test)] + pub(crate) fn size() -> usize { + 192 + } fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1595,19 +1611,8 @@ pub mod g2 { }) } } - } - - impl EncodedPoint for G2Uncompressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Uncompressed([0; 192]) - } - fn size() -> usize { - 192 - } fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::empty(); + let mut res = Self::default(); if affine.is_identity().into() { // Set the second-most significant bit to indicate this point @@ -1627,6 +1632,12 @@ pub mod g2 { #[derive(Copy, Clone)] pub struct G2Compressed([u8; 96]); + impl Default for G2Compressed { + fn default() -> Self { + G2Compressed([0; 96]) + } + } + impl AsRef<[u8]> for G2Compressed { fn as_ref(&self) -> &[u8] { &self.0 @@ -1646,6 +1657,10 @@ pub mod g2 { } impl G2Compressed { + #[cfg(test)] + pub(crate) fn size() -> usize { + 96 + } fn into_affine_unchecked(&self) -> Result { // Create a copy of this representation. let mut copy = self.0; @@ -1701,19 +1716,8 @@ pub mod g2 { } } } - } - - impl EncodedPoint for G2Compressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Compressed([0; 96]) - } - fn size() -> usize { - 96 - } fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::empty(); + let mut res = Self::default(); if affine.is_identity().into() { // Set the second-most significant bit to indicate this point diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 815c2bb4f5..45dcb7e02f 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, EncodedPoint, Group}; +use group::{CurveAffine, CurveProjective, Group}; use super::*; use crate::*; @@ -57,20 +57,21 @@ fn test_pairing_result_against_relic() { fn uncompressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); + let encoded_len = ::Uncompressed::default() + .as_ref() + .len(); let mut v = vec![]; { let mut expected = expected; for _ in 0..1000 { let e_affine = e.into_affine(); - let encoded = ::Uncompressed::from_affine(e_affine); + let encoded = e_affine.into_uncompressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Uncompressed::empty(); - decoded - .as_mut() - .copy_from_slice(&expected[0..::Uncompressed::size()]); - expected = &expected[::Uncompressed::size()..]; + let mut decoded = ::Uncompressed::default(); + decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); + expected = &expected[encoded_len..]; let decoded = G::Affine::from_uncompressed(&decoded).unwrap(); assert_eq!(e_affine, decoded); @@ -83,20 +84,21 @@ fn uncompressed_test_vectors(expected: &[u8]) { fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); + let encoded_len = ::Compressed::default() + .as_ref() + .len(); let mut v = vec![]; { let mut expected = expected; for _ in 0..1000 { let e_affine = e.into_affine(); - let encoded = ::Compressed::from_affine(e_affine); + let encoded = e_affine.into_compressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Compressed::empty(); - decoded - .as_mut() - .copy_from_slice(&expected[0..::Compressed::size()]); - expected = &expected[::Compressed::size()..]; + let mut decoded = ::Compressed::default(); + decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); + expected = &expected[encoded_len..]; let decoded = G::Affine::from_compressed(&decoded).unwrap(); assert_eq!(e_affine, decoded); From 5f1607c9b5a5caafbc65f5f6f560f1b4512a928e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 20 May 2020 11:30:41 +1200 Subject: [PATCH 039/210] group: Rename into_*(&self) -> to_*(&self) Rust naming convention uses the into_ prefix for methods that consume self, and the to_ prefix for methods that take an immutable reference. --- bellman/src/groth16/generator.rs | 16 +++---- bellman/src/groth16/mod.rs | 30 ++++++------- bellman/src/groth16/prover.rs | 6 +-- bellman/src/groth16/tests/dummy_engine.rs | 10 ++--- bellman/src/groth16/verifier.rs | 4 +- bellman/src/multiexp.rs | 2 +- group/src/lib.rs | 8 ++-- group/src/tests/mod.rs | 54 +++++++++++------------ pairing/src/bls12_381/ec.rs | 24 +++++----- pairing/src/bls12_381/tests/mod.rs | 24 +++++----- pairing/src/tests/engine.rs | 24 +++++----- 11 files changed, 99 insertions(+), 103 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 11da63fcf5..7a0e1b0b9b 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -459,16 +459,16 @@ where } } - let g1 = g1.into_affine(); - let g2 = g2.into_affine(); + let g1 = g1.to_affine(); + let g2 = g2.to_affine(); let vk = VerifyingKey:: { - alpha_g1: (g1 * &alpha).into_affine(), - beta_g1: (g1 * &beta).into_affine(), - beta_g2: (g2 * &beta).into_affine(), - gamma_g2: (g2 * &gamma).into_affine(), - delta_g1: (g1 * &delta).into_affine(), - delta_g2: (g2 * &delta).into_affine(), + alpha_g1: (g1 * &alpha).to_affine(), + beta_g1: (g1 * &beta).to_affine(), + beta_g2: (g2 * &beta).to_affine(), + gamma_g2: (g2 * &gamma).to_affine(), + delta_g1: (g1 * &delta).to_affine(), + delta_g2: (g2 * &delta).to_affine(), ic, }; diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 42b96d90e2..a5d8aa5224 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -38,9 +38,9 @@ impl PartialEq for Proof { impl Proof { pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.a.into_compressed().as_ref())?; - writer.write_all(self.b.into_compressed().as_ref())?; - writer.write_all(self.c.into_compressed().as_ref())?; + writer.write_all(self.a.to_compressed().as_ref())?; + writer.write_all(self.b.to_compressed().as_ref())?; + writer.write_all(self.c.to_compressed().as_ref())?; Ok(()) } @@ -142,15 +142,15 @@ impl PartialEq for VerifyingKey { impl VerifyingKey { pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; - writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; - writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; - writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?; - writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; - writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; + writer.write_all(self.alpha_g1.to_uncompressed().as_ref())?; + writer.write_all(self.beta_g1.to_uncompressed().as_ref())?; + writer.write_all(self.beta_g2.to_uncompressed().as_ref())?; + writer.write_all(self.gamma_g2.to_uncompressed().as_ref())?; + writer.write_all(self.delta_g1.to_uncompressed().as_ref())?; + writer.write_all(self.delta_g2.to_uncompressed().as_ref())?; writer.write_u32::(self.ic.len() as u32)?; for ic in &self.ic { - writer.write_all(ic.into_uncompressed().as_ref())?; + writer.write_all(ic.to_uncompressed().as_ref())?; } Ok(()) @@ -261,27 +261,27 @@ impl Parameters { writer.write_u32::(self.h.len() as u32)?; for g in &self.h[..] { - writer.write_all(g.into_uncompressed().as_ref())?; + writer.write_all(g.to_uncompressed().as_ref())?; } writer.write_u32::(self.l.len() as u32)?; for g in &self.l[..] { - writer.write_all(g.into_uncompressed().as_ref())?; + writer.write_all(g.to_uncompressed().as_ref())?; } writer.write_u32::(self.a.len() as u32)?; for g in &self.a[..] { - writer.write_all(g.into_uncompressed().as_ref())?; + writer.write_all(g.to_uncompressed().as_ref())?; } writer.write_u32::(self.b_g1.len() as u32)?; for g in &self.b_g1[..] { - writer.write_all(g.into_uncompressed().as_ref())?; + writer.write_all(g.to_uncompressed().as_ref())?; } writer.write_u32::(self.b_g2.len() as u32)?; for g in &self.b_g2[..] { - writer.write_all(g.into_uncompressed().as_ref())?; + writer.write_all(g.to_uncompressed().as_ref())?; } Ok(()) diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 262bb75c4c..0052751e9b 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -332,8 +332,8 @@ where AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); Ok(Proof { - a: g_a.into_affine(), - b: g_b.into_affine(), - c: g_c.into_affine(), + a: g_a.to_affine(), + b: g_b.to_affine(), + c: g_c.to_affine(), }) } diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index c00d42a9a2..5686c95c45 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -400,11 +400,11 @@ impl CurveProjective for Fr { assert_eq!(p.len(), q.len()); for (p, q) in p.iter().zip(q.iter_mut()) { - *q = p.into_affine(); + *q = p.to_affine(); } } - fn into_affine(&self) -> Fr { + fn to_affine(&self) -> Fr { *self } @@ -451,7 +451,7 @@ impl CurveAffine for Fr { Choice::from(if ::is_zero(self) { 1 } else { 0 }) } - fn into_projective(&self) -> Self::Projective { + fn to_projective(&self) -> Self::Projective { *self } @@ -463,7 +463,7 @@ impl CurveAffine for Fr { unimplemented!() } - fn into_compressed(&self) -> Self::Compressed { + fn to_compressed(&self) -> Self::Compressed { unimplemented!() } @@ -475,7 +475,7 @@ impl CurveAffine for Fr { unimplemented!() } - fn into_uncompressed(&self) -> Self::Uncompressed { + fn to_uncompressed(&self) -> Self::Uncompressed { unimplemented!() } } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index f80f143134..ae55b14097 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -27,7 +27,7 @@ pub fn verify_proof<'a, E: Engine>( return Err(SynthesisError::MalformedVerifyingKey); } - let mut acc = pvk.ic[0].into_projective(); + let mut acc = pvk.ic[0].to_projective(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i)); @@ -44,7 +44,7 @@ pub fn verify_proof<'a, E: Engine>( Ok(E::final_exponentiation(&E::miller_loop( [ (&proof.a.prepare(), &proof.b.prepare()), - (&acc.into_affine().prepare(), &pvk.neg_gamma_g2), + (&acc.to_affine().prepare(), &pvk.neg_gamma_g2), (&proof.c.prepare(), &pvk.neg_delta_g2), ] .iter(), diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 40edb6b072..a9a868f1ad 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -328,7 +328,7 @@ fn test_with_bls12() { ); let g = Arc::new( (0..SAMPLES) - .map(|_| ::G1::random(rng).into_affine()) + .map(|_| ::G1::random(rng).to_affine()) .collect::>(), ); diff --git a/group/src/lib.rs b/group/src/lib.rs index 13a17c8e3f..b2e83b30de 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -107,7 +107,7 @@ pub trait CurveProjective: fn batch_normalize(p: &[Self], q: &mut [Self::Affine]); /// Converts this element into its affine representation. - fn into_affine(&self) -> Self::Affine; + fn to_affine(&self) -> Self::Affine; /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. @@ -152,7 +152,7 @@ pub trait CurveAffine: fn is_identity(&self) -> Choice; /// Converts this element into its affine representation. - fn into_projective(&self) -> Self::Projective; + fn to_projective(&self) -> Self::Projective; /// Attempts to deserialize an element from its compressed encoding. fn from_compressed(bytes: &Self::Compressed) -> CtOption; @@ -167,7 +167,7 @@ pub trait CurveAffine: /// Converts this element into its compressed encoding, so long as it's not /// the point at infinity. - fn into_compressed(&self) -> Self::Compressed; + fn to_compressed(&self) -> Self::Compressed; /// Attempts to deserialize an element from its uncompressed encoding. fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; @@ -182,5 +182,5 @@ pub trait CurveAffine: /// Converts this element into its uncompressed encoding, so long as it's not /// the point at infinity. - fn into_uncompressed(&self) -> Self::Uncompressed; + fn to_uncompressed(&self) -> Self::Uncompressed; } diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 53fb483810..406ae6e3b4 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -41,7 +41,7 @@ pub fn curve_tests() { let mut z2 = z; z2.add_assign(&r); - z.add_assign(&r.into_affine()); + z.add_assign(&r.to_affine()); assert_eq!(z, z2); assert_eq!(z, r); @@ -50,12 +50,8 @@ pub fn curve_tests() { // Transformations { let a = G::random(&mut rng); - let b = a.into_affine().into_projective(); - let c = a - .into_affine() - .into_projective() - .into_affine() - .into_projective(); + let b = a.to_affine().to_projective(); + let c = a.to_affine().to_projective().to_affine().to_projective(); assert_eq!(a, b); assert_eq!(b, c); } @@ -211,7 +207,7 @@ fn random_negation_tests() { assert!(bool::from(t3.is_identity())); let mut t4 = t1; - t4.add_assign(&t2.into_affine()); + t4.add_assign(&t2.to_affine()); assert!(bool::from(t4.is_identity())); assert_eq!(t1.neg(), t2); @@ -239,7 +235,7 @@ fn random_doubling_tests() { tmp2.add_assign(&b); let mut tmp3 = a; - tmp3.add_assign(&b.into_affine()); + tmp3.add_assign(&b.to_affine()); assert_eq!(tmp1, tmp2); assert_eq!(tmp1, tmp3); @@ -255,8 +251,8 @@ fn random_multiplication_tests() { for _ in 0..1000 { let mut a = G::random(&mut rng); let mut b = G::random(&mut rng); - let a_affine = a.into_affine(); - let b_affine = b.into_affine(); + let a_affine = a.to_affine(); + let b_affine = b.to_affine(); let s = G::Scalar::random(&mut rng); @@ -291,9 +287,9 @@ fn random_addition_tests() { let a = G::random(&mut rng); let b = G::random(&mut rng); let c = G::random(&mut rng); - let a_affine = a.into_affine(); - let b_affine = b.into_affine(); - let c_affine = c.into_affine(); + let a_affine = a.to_affine(); + let b_affine = b.to_affine(); + let c_affine = c.to_affine(); // a + a should equal the doubling { @@ -301,7 +297,7 @@ fn random_addition_tests() { aplusa.add_assign(&a); let mut aplusamixed = a; - aplusamixed.add_assign(&a.into_affine()); + aplusamixed.add_assign(&a.to_affine()); let adouble = a.double(); @@ -329,17 +325,17 @@ fn random_addition_tests() { // Mixed addition // (a + b) + c - tmp[3] = a_affine.into_projective(); + tmp[3] = a_affine.to_projective(); tmp[3].add_assign(&b_affine); tmp[3].add_assign(&c_affine); // a + (b + c) - tmp[4] = b_affine.into_projective(); + tmp[4] = b_affine.to_projective(); tmp[4].add_assign(&c_affine); tmp[4].add_assign(&a_affine); // (a + c) + b - tmp[5] = a_affine.into_projective(); + tmp[5] = a_affine.to_projective(); tmp[5].add_assign(&c_affine); tmp[5].add_assign(&b_affine); @@ -347,7 +343,7 @@ fn random_addition_tests() { for i in 0..6 { for j in 0..6 { assert_eq!(tmp[i], tmp[j]); - assert_eq!(tmp[i].into_affine(), tmp[j].into_affine()); + assert_eq!(tmp[i].to_affine(), tmp[j].to_affine()); } assert!(tmp[i] != a); @@ -369,8 +365,8 @@ fn random_transformation_tests() { for _ in 0..1000 { let g = G::random(&mut rng); - let g_affine = g.into_affine(); - let g_projective = g_affine.into_projective(); + let g_affine = g.to_affine(); + let g_projective = g_affine.to_projective(); assert_eq!(g, g_projective); } @@ -386,10 +382,10 @@ fn random_transformation_tests() { } for _ in 0..5 { let s = between.sample(&mut rng); - v[s] = v[s].into_affine().into_projective(); + v[s] = v[s].to_affine().to_projective(); } - let expected_v = v.iter().map(|v| v.into_affine()).collect::>(); + let expected_v = v.iter().map(|v| v.to_affine()).collect::>(); let mut normalized = vec![G::Affine::identity(); v.len()]; G::batch_normalize(&v, &mut normalized); @@ -405,29 +401,29 @@ fn random_encoding_tests() { ]); assert_eq!( - G::Affine::from_uncompressed(&G::Affine::identity().into_uncompressed()).unwrap(), + G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), G::Affine::identity() ); assert_eq!( - G::Affine::from_compressed(&G::Affine::identity().into_compressed()).unwrap(), + G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(), G::Affine::identity() ); for _ in 0..1000 { - let mut r = G::random(&mut rng).into_affine(); + let mut r = G::random(&mut rng).to_affine(); - let uncompressed = r.into_uncompressed(); + let uncompressed = r.to_uncompressed(); let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); assert_eq!(de_uncompressed, r); - let compressed = r.into_compressed(); + let compressed = r.to_compressed(); let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); r = r.neg(); - let compressed = r.into_compressed(); + let compressed = r.to_compressed(); let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 5225535d71..2618079907 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -57,7 +57,7 @@ macro_rules! curve_impl { impl ::std::fmt::Display for $projective { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}", self.into_affine()) + write!(f, "{}", self.to_affine()) } } @@ -221,7 +221,7 @@ macro_rules! curve_impl { Choice::from(if self.infinity { 1 } else { 0 }) } - fn into_projective(&self) -> $projective { + fn to_projective(&self) -> $projective { (*self).into() } @@ -247,7 +247,7 @@ macro_rules! curve_impl { } } - fn into_compressed(&self) -> Self::Compressed { + fn to_compressed(&self) -> Self::Compressed { $compressed::from_affine(*self) } @@ -276,7 +276,7 @@ macro_rules! curve_impl { } } - fn into_uncompressed(&self) -> Self::Uncompressed { + fn to_uncompressed(&self) -> Self::Uncompressed { $uncompressed::from_affine(*self) } } @@ -795,7 +795,7 @@ macro_rules! curve_impl { } } - fn into_affine(&self) -> $affine { + fn to_affine(&self) -> $affine { (*self).into() } @@ -1476,15 +1476,15 @@ pub mod g1 { assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); - let mut tmp1 = a.into_projective(); - tmp1.add_assign(&b.into_projective()); - assert_eq!(tmp1.into_affine(), c); - assert_eq!(tmp1, c.into_projective()); + let mut tmp1 = a.to_projective(); + tmp1.add_assign(&b.to_projective()); + assert_eq!(tmp1.to_affine(), c); + assert_eq!(tmp1, c.to_projective()); - let mut tmp2 = a.into_projective(); + let mut tmp2 = a.to_projective(); tmp2.add_assign(&b); - assert_eq!(tmp2.into_affine(), c); - assert_eq!(tmp2, c.into_projective()); + assert_eq!(tmp2.to_affine(), c); + assert_eq!(tmp2, c.to_projective()); } #[test] diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 45dcb7e02f..7b4bbe7aa9 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -65,8 +65,8 @@ fn uncompressed_test_vectors(expected: &[u8]) { { let mut expected = expected; for _ in 0..1000 { - let e_affine = e.into_affine(); - let encoded = e_affine.into_uncompressed(); + let e_affine = e.to_affine(); + let encoded = e_affine.to_uncompressed(); v.extend_from_slice(encoded.as_ref()); let mut decoded = ::Uncompressed::default(); @@ -92,8 +92,8 @@ fn compressed_test_vectors(expected: &[u8]) { { let mut expected = expected; for _ in 0..1000 { - let e_affine = e.into_affine(); - let encoded = e_affine.into_compressed(); + let e_affine = e.to_affine(); + let encoded = e_affine.to_compressed(); v.extend_from_slice(encoded.as_ref()); let mut decoded = ::Compressed::default(); @@ -132,7 +132,7 @@ fn test_g2_compressed_valid_vectors() { #[test] fn test_g1_uncompressed_invalid_vectors() { { - let z = G1Affine::identity().into_uncompressed(); + let z = G1Affine::identity().to_uncompressed(); { let mut z = z; @@ -165,7 +165,7 @@ fn test_g1_uncompressed_invalid_vectors() { } } - let o = G1Affine::generator().into_uncompressed(); + let o = G1Affine::generator().to_uncompressed(); { let mut o = o; @@ -248,7 +248,7 @@ fn test_g1_uncompressed_invalid_vectors() { #[test] fn test_g2_uncompressed_invalid_vectors() { { - let z = G2Affine::identity().into_uncompressed(); + let z = G2Affine::identity().to_uncompressed(); { let mut z = z; @@ -281,7 +281,7 @@ fn test_g2_uncompressed_invalid_vectors() { } } - let o = G2Affine::generator().into_uncompressed(); + let o = G2Affine::generator().to_uncompressed(); { let mut o = o; @@ -392,7 +392,7 @@ fn test_g2_uncompressed_invalid_vectors() { #[test] fn test_g1_compressed_invalid_vectors() { { - let z = G1Affine::identity().into_compressed(); + let z = G1Affine::identity().to_compressed(); { let mut z = z; @@ -425,7 +425,7 @@ fn test_g1_compressed_invalid_vectors() { } } - let o = G1Affine::generator().into_compressed(); + let o = G1Affine::generator().to_compressed(); { let mut o = o; @@ -506,7 +506,7 @@ fn test_g1_compressed_invalid_vectors() { #[test] fn test_g2_compressed_invalid_vectors() { { - let z = G2Affine::identity().into_compressed(); + let z = G2Affine::identity().to_compressed(); { let mut z = z; @@ -539,7 +539,7 @@ fn test_g2_compressed_invalid_vectors() { } } - let o = G2Affine::generator().into_compressed(); + let o = G2Affine::generator().to_compressed(); { let mut o = o; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 0a366e06b0..6af1b801c1 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -13,8 +13,8 @@ pub fn engine_tests() { ]); for _ in 0..10 { - let a = E::G1::random(&mut rng).into_affine(); - let b = E::G2::random(&mut rng).into_affine(); + let a = E::G1::random(&mut rng).to_affine(); + let b = E::G2::random(&mut rng).to_affine(); assert!(a.pairing_with(&b) == b.pairing_with(&a)); assert!(a.pairing_with(&b) == E::pairing(a, b)); @@ -24,10 +24,10 @@ pub fn engine_tests() { let z1 = E::G1Affine::identity().prepare(); let z2 = E::G2Affine::identity().prepare(); - let a = E::G1::random(&mut rng).into_affine().prepare(); - let b = E::G2::random(&mut rng).into_affine().prepare(); - let c = E::G1::random(&mut rng).into_affine().prepare(); - let d = E::G2::random(&mut rng).into_affine().prepare(); + let a = E::G1::random(&mut rng).to_affine().prepare(); + let b = E::G2::random(&mut rng).to_affine().prepare(); + let c = E::G1::random(&mut rng).to_affine().prepare(); + let d = E::G2::random(&mut rng).to_affine().prepare(); assert_eq!( E::Fqk::one(), @@ -67,8 +67,8 @@ fn random_miller_loop_tests() { let p2 = E::pairing(a, b); - let a = a.into_affine().prepare(); - let b = b.into_affine().prepare(); + let a = a.to_affine().prepare(); + let b = b.to_affine().prepare(); let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap(); @@ -88,10 +88,10 @@ fn random_miller_loop_tests() { let mut abcd = ab; abcd.mul_assign(&cd); - let a = a.into_affine().prepare(); - let b = b.into_affine().prepare(); - let c = c.into_affine().prepare(); - let d = d.into_affine().prepare(); + let a = a.to_affine().prepare(); + let b = b.to_affine().prepare(); + let c = c.to_affine().prepare(); + let d = d.to_affine().prepare(); let abcd_with_double_loop = E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap(); From 4edff963210fd2f3c6e5e0dd2a6a984aa83c8d6f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 22 May 2020 18:52:35 +1200 Subject: [PATCH 040/210] Fix various lints --- bellman/src/lib.rs | 2 +- pairing/src/bls12_381/ec.rs | 2 +- zcash_primitives/src/lib.rs | 2 +- zcash_primitives/src/note_encryption.rs | 14 ++++---------- zcash_primitives/src/transaction/components.rs | 4 ++-- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index 1e48b0c790..4c6c7eaf83 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -340,7 +340,7 @@ impl fmt::Display for SynthesisError { write!(f, "I/O error: ")?; e.fmt(f) } else { - write!(f, "{}", self.description()) + write!(f, "{}", self) } } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 2618079907..b14f04f4fd 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -899,7 +899,7 @@ impl fmt::Display for GroupDecodingError { GroupDecodingError::CoordinateDecodingError(description) => { write!(f, "{} decoding error", description) } - _ => write!(f, "{}", self.description()), + _ => write!(f, "{}", self), } } } diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 8175a2628e..0050bbd952 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -33,5 +33,5 @@ mod test_vectors; use crate::jubjub::JubjubBls12; lazy_static! { - pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; + pub static ref JUBJUB: JubjubBls12 = JubjubBls12::new(); } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 1ad6cce2b1..c05a5c887f 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -23,19 +23,13 @@ use crate::{keys::OutgoingViewingKey, JUBJUB}; pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; -const COMPACT_NOTE_SIZE: usize = ( - 1 + // version +const COMPACT_NOTE_SIZE: usize = 1 + // version 11 + // diversifier 8 + // value - 32 - // rcv -); + 32; // rcv const NOTE_PLAINTEXT_SIZE: usize = COMPACT_NOTE_SIZE + 512; -const OUT_PLAINTEXT_SIZE: usize = ( - 32 + // pk_d - 32 - // esk -); +const OUT_PLAINTEXT_SIZE: usize = 32 + // pk_d + 32; // esk const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + 16; const OUT_CIPHERTEXT_SIZE: usize = OUT_PLAINTEXT_SIZE + 16; diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 25baa28fee..5881401ee9 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -14,9 +14,9 @@ pub mod amount; pub use self::amount::Amount; // π_A + π_B + π_C -pub const GROTH_PROOF_SIZE: usize = (48 + 96 + 48); +pub const GROTH_PROOF_SIZE: usize = 48 + 96 + 48; // π_A + π_A' + π_B + π_B' + π_C + π_C' + π_K + π_H -const PHGR_PROOF_SIZE: usize = (33 + 33 + 65 + 33 + 33 + 33 + 33 + 33); +const PHGR_PROOF_SIZE: usize = 33 + 33 + 65 + 33 + 33 + 33 + 33 + 33; const ZC_NUM_JS_INPUTS: usize = 2; const ZC_NUM_JS_OUTPUTS: usize = 2; From 2892cf94c1575fa9ebb939ca4d3fa6145c3651f0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 22 May 2020 18:55:46 +1200 Subject: [PATCH 041/210] Migrate to protobuf-codegen-pure 2.14 --- zcash_client_backend/Cargo.toml | 2 +- zcash_client_backend/build.rs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 562d1a50e5..a0bebde72c 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -21,7 +21,7 @@ subtle = "2" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [build-dependencies] -protobuf-codegen-pure = "2" +protobuf-codegen-pure = "2.14" [dev-dependencies] rand_core = "0.5" diff --git a/zcash_client_backend/build.rs b/zcash_client_backend/build.rs index 41e021444f..d1014dbedb 100644 --- a/zcash_client_backend/build.rs +++ b/zcash_client_backend/build.rs @@ -1,11 +1,10 @@ use protobuf_codegen_pure; fn main() { - protobuf_codegen_pure::run(protobuf_codegen_pure::Args { - out_dir: "src/proto", - input: &["proto/compact_formats.proto"], - includes: &["proto"], - customize: Default::default(), - }) - .expect("protoc"); + protobuf_codegen_pure::Codegen::new() + .out_dir("src/proto") + .inputs(&["proto/compact_formats.proto"]) + .includes(&["proto"]) + .run() + .expect("Protobuf codegen failed"); } From c4887320220b22c54efd305c5d66e8ede22d8e37 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 22 May 2020 19:13:33 +1200 Subject: [PATCH 042/210] Use text directive on sage script doc comment instead of norun norun is an invalid directive, and no_run would cause the sage script to be compiled as Rust. --- bls12_381/src/notes/design.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs index d245260ef6..e5f8b9cbb4 100644 --- a/bls12_381/src/notes/design.rs +++ b/bls12_381/src/notes/design.rs @@ -18,7 +18,7 @@ //! //! This can be derived using the following sage script: //! -//! ```norun +//! ```text //! param = -0xd201000000010000 //! def r(x): //! return (x**4) - (x**2) + 1 From 4b1d8e5226f32b251189433e686e6694b870a012 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Fri, 22 May 2020 18:02:44 -0700 Subject: [PATCH 043/210] Add methods to get OutPoint n, hash --- zcash_primitives/src/transaction/components.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 5881401ee9..abcffcb8f8 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -43,6 +43,14 @@ impl OutPoint { writer.write_all(&self.hash)?; writer.write_u32::(self.n) } + + pub fn n(&self) -> u32 { + self.n + } + + pub fn hash(&self) -> &[u8; 32] { + &self.hash + } } #[derive(Debug)] From f208c498cfe4d5a568706b535b1dd9148ad7e193 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 16:51:07 +1200 Subject: [PATCH 044/210] bls12_381: Fix typo in Fp::from_bytes documentation --- bls12_381/src/fp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs index 28aa24bf88..02d1d78558 100644 --- a/bls12_381/src/fp.rs +++ b/bls12_381/src/fp.rs @@ -162,7 +162,7 @@ impl Fp { self.ct_eq(&Fp::zero()) } - /// Attempts to convert a little-endian byte representation of + /// Attempts to convert a big-endian byte representation of /// a scalar into an `Fp`, failing if the input is not canonical. pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { let mut tmp = Fp([0, 0, 0, 0, 0, 0]); From 463d72cc3e00c9e73d751fe9a2f53477c9ea7ab1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 16:51:55 +1200 Subject: [PATCH 045/210] bls12_381: Implement ff traits for Scalar --- bls12_381/Cargo.toml | 13 +++++ bls12_381/src/scalar.rs | 108 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 6e77fb51b3..db808a2a5d 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -20,6 +20,19 @@ name = "groups" harness = false required-features = ["groups"] +[dependencies.byteorder] +version = "1" +default-features = false + +[dependencies.ff] +path = "../ff" +version = "0.6" +default-features = false + +[dependencies.rand_core] +version = "0.5" +default-features = false + [dependencies.subtle] version = "2.2.1" default-features = false diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs index 91f88a49b8..e13231dd9d 100644 --- a/bls12_381/src/scalar.rs +++ b/bls12_381/src/scalar.rs @@ -4,7 +4,9 @@ use core::convert::TryFrom; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use rand_core::RngCore; +use ff::{Field, PrimeField}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::util::{adc, mac, sbb}; @@ -28,6 +30,12 @@ impl fmt::Debug for Scalar { } } +impl fmt::Display for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl From for Scalar { fn from(val: u64) -> Scalar { Scalar([val, 0, 0, 0]) * R2 @@ -70,6 +78,22 @@ const MODULUS: Scalar = Scalar([ 0x73ed_a753_299d_7d48, ]); +const MODULUS_BYTES: [u8; 32] = [ + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, 0x53, + 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, 0xed, 0x73, +]; + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 255; + +// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: Scalar = Scalar([ + 0x0000_000e_ffff_fff1, + 0x17e3_63d3_0018_9c0f, + 0xff9c_5787_6f84_57b0, + 0x3513_3220_8fc5_a8c4, +]); + impl<'a> Neg for &'a Scalar { type Output = Scalar; @@ -145,6 +169,7 @@ const R3: Scalar = Scalar([ 0x6e2a_5bb9_c8db_33e9, ]); +// 2^S * t = MODULUS - 1 with t odd const S: u32 = 32; /// GENERATOR^t where t * 2^s + 1 = q @@ -613,12 +638,95 @@ impl Scalar { } } +impl From for [u8; 32] { + fn from(value: Scalar) -> [u8; 32] { + value.to_bytes() + } +} + impl<'a> From<&'a Scalar> for [u8; 32] { fn from(value: &'a Scalar) -> [u8; 32] { value.to_bytes() } } +impl Field for Scalar { + fn random(rng: &mut R) -> Self { + let mut buf = [0; 64]; + rng.fill_bytes(&mut buf); + Self::from_bytes_wide(&buf) + } + + fn zero() -> Self { + Self::zero() + } + + fn one() -> Self { + Self::one() + } + + fn is_zero(&self) -> bool { + self.ct_eq(&Self::zero()).into() + } + + #[must_use] + fn square(&self) -> Self { + self.square() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt(&self) -> CtOption { + self.sqrt() + } +} + +impl PrimeField for Scalar { + type Repr = [u8; 32]; + type ReprEndianness = byteorder::LittleEndian; + + fn from_repr(r: Self::Repr) -> Option { + let res = Self::from_bytes(&r); + if res.is_some().into() { + Some(res.unwrap()) + } else { + None + } + } + + fn to_repr(&self) -> Self::Repr { + self.to_bytes() + } + + fn is_odd(&self) -> bool { + self.to_bytes()[0] & 1 == 1 + } + + fn char() -> Self::Repr { + MODULUS_BYTES + } + + const NUM_BITS: u32 = MODULUS_BITS; + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + GENERATOR + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + ROOT_OF_UNITY + } +} + #[test] fn test_inv() { // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating From b0542dd6d395f201bf1f78693d92c14a7d025ccf Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 2 May 2020 16:52:29 +1200 Subject: [PATCH 046/210] jubjub: Implement ff traits for Fr --- jubjub/Cargo.toml | 13 +++++ jubjub/src/fr.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index 31221efc77..19976d7488 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -18,6 +18,19 @@ path = "../bls12_381" version = "0.1" default-features = false +[dependencies.byteorder] +version = "1" +default-features = false + +[dependencies.ff] +path = "../ff" +version = "0.6" +default-features = false + +[dependencies.rand_core] +version = "0.5" +default-features = false + [dependencies.subtle] version = "^2.2.1" default-features = false diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs index e46953027e..827793326a 100644 --- a/jubjub/src/fr.rs +++ b/jubjub/src/fr.rs @@ -5,6 +5,8 @@ use core::convert::TryInto; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use ff::{Field, PrimeField}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::util::{adc, mac, sbb}; @@ -28,6 +30,12 @@ impl fmt::Debug for Fr { } } +impl fmt::Display for Fr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl From for Fr { fn from(val: u64) -> Fr { Fr([val, 0, 0, 0]) * R2 @@ -70,6 +78,33 @@ pub const MODULUS: Fr = Fr([ 0x0e7d_b4ea_6533_afa9, ]); +const MODULUS_BYTES: [u8; 32] = [ + 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, + 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, +]; + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 252; + +// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: Fr = Fr([ + 0x720b_1b19_d49e_a8f1, + 0xbf4a_a361_01f1_3a58, + 0x5fa8_cc96_8193_ccbb, + 0x0e70_cbdc_7dcc_f3ac, +]); + +// 2^S * t = MODULUS - 1 with t odd +const S: u32 = 1; + +// 2^S root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: Fr = Fr([ + 0xaa9f_02ab_1d61_24de, + 0xb352_4a64_6611_2932, + 0x7342_2612_15ac_260b, + 0x04d6_b87b_1da2_59e2, +]); + impl<'a> Neg for &'a Fr { type Output = Fr; @@ -575,12 +610,95 @@ impl Fr { } } +impl From for [u8; 32] { + fn from(value: Fr) -> [u8; 32] { + value.to_bytes() + } +} + impl<'a> From<&'a Fr> for [u8; 32] { fn from(value: &'a Fr) -> [u8; 32] { value.to_bytes() } } +impl Field for Fr { + fn random(rng: &mut R) -> Self { + let mut buf = [0; 64]; + rng.fill_bytes(&mut buf); + Self::from_bytes_wide(&buf) + } + + fn zero() -> Self { + Self::zero() + } + + fn one() -> Self { + Self::one() + } + + fn is_zero(&self) -> bool { + self.ct_eq(&Self::zero()).into() + } + + #[must_use] + fn square(&self) -> Self { + self.square() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } + + fn invert(&self) -> CtOption { + self.invert() + } + + fn sqrt(&self) -> CtOption { + self.sqrt() + } +} + +impl PrimeField for Fr { + type Repr = [u8; 32]; + type ReprEndianness = byteorder::LittleEndian; + + fn from_repr(r: Self::Repr) -> Option { + let res = Self::from_bytes(&r); + if res.is_some().into() { + Some(res.unwrap()) + } else { + None + } + } + + fn to_repr(&self) -> Self::Repr { + self.to_bytes() + } + + fn is_odd(&self) -> bool { + self.to_bytes()[0] & 1 == 1 + } + + fn char() -> Self::Repr { + MODULUS_BYTES + } + + const NUM_BITS: u32 = MODULUS_BITS; + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + GENERATOR + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + ROOT_OF_UNITY + } +} + #[test] fn test_inv() { // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating From 0b2293bcc0f86b42f8c5ebad750e07ae87cd0b3a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 12:32:21 +1200 Subject: [PATCH 047/210] bellman: Replace E: ScalarEngine with Scalar: PrimeField Instead of imposing the requirement that bellman users explicitly specify an engine for every proving system, we allow the Rust type system to figure it out for us. An engine is specifically useful in places where we require defined relationships between several types; ff::ScalarEngine only has one type, and thus any usage of it can be trivially replaced by an explicit Scalar type. This is also more readable :) --- bellman/src/domain.rs | 120 +++++------ bellman/src/gadgets/blake2s.rs | 28 +-- bellman/src/gadgets/boolean.rs | 192 ++++++++++-------- bellman/src/gadgets/lookup.rs | 86 ++++---- bellman/src/gadgets/multieq.rs | 36 ++-- bellman/src/gadgets/multipack.rs | 31 ++- bellman/src/gadgets/num.rs | 89 ++++---- bellman/src/gadgets/sha256.rs | 36 ++-- bellman/src/gadgets/test/mod.rs | 99 +++++---- bellman/src/gadgets/uint32.rs | 68 ++++--- bellman/src/groth16/generator.rs | 58 +++--- bellman/src/groth16/mod.rs | 12 +- bellman/src/groth16/prover.rs | 44 ++-- bellman/src/groth16/tests/mod.rs | 25 ++- bellman/src/lib.rs | 144 +++++++------ bellman/tests/mimc.rs | 28 +-- zcash_proofs/examples/bench.rs | 4 +- zcash_proofs/src/circuit/ecc.rs | 82 ++++---- zcash_proofs/src/circuit/pedersen_hash.rs | 14 +- zcash_proofs/src/circuit/sapling.rs | 46 ++--- zcash_proofs/src/circuit/sprout/commitment.rs | 8 +- zcash_proofs/src/circuit/sprout/input.rs | 24 +-- zcash_proofs/src/circuit/sprout/mod.rs | 41 ++-- zcash_proofs/src/circuit/sprout/output.rs | 8 +- zcash_proofs/src/circuit/sprout/prfs.rs | 32 +-- zcash_proofs/src/sapling/prover.rs | 2 +- zcash_proofs/src/sapling/verifier.rs | 2 +- zcash_proofs/src/sprout.rs | 2 +- 28 files changed, 696 insertions(+), 665 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 4e24f18b0b..47f8ff083b 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -11,41 +11,40 @@ //! [`EvaluationDomain`]: crate::domain::EvaluationDomain //! [Groth16]: https://eprint.iacr.org/2016/260 -use ff::{Field, PrimeField, ScalarEngine}; +use ff::PrimeField; use group::CurveProjective; -use std::ops::{AddAssign, MulAssign, SubAssign}; use super::SynthesisError; use super::multicore::Worker; -pub struct EvaluationDomain> { +pub struct EvaluationDomain> { coeffs: Vec, exp: u32, - omega: E::Fr, - omegainv: E::Fr, - geninv: E::Fr, - minv: E::Fr, + omega: S, + omegainv: S, + geninv: S, + minv: S, } -impl> AsRef<[G]> for EvaluationDomain { +impl> AsRef<[G]> for EvaluationDomain { fn as_ref(&self) -> &[G] { &self.coeffs } } -impl> AsMut<[G]> for EvaluationDomain { +impl> AsMut<[G]> for EvaluationDomain { fn as_mut(&mut self) -> &mut [G] { &mut self.coeffs } } -impl> EvaluationDomain { +impl> EvaluationDomain { pub fn into_coeffs(self) -> Vec { self.coeffs } - pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { + pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { // Compute the size of our evaluation domain let mut m = 1; let mut exp = 0; @@ -55,14 +54,14 @@ impl> EvaluationDomain { // The pairing-friendly curve may not be able to support // large enough (radix2) evaluation domains. - if exp >= E::Fr::S { + if exp >= S::S { return Err(SynthesisError::PolynomialDegreeTooLarge); } } // Compute omega, the 2^exp primitive root of unity - let mut omega = E::Fr::root_of_unity(); - for _ in exp..E::Fr::S { + let mut omega = S::root_of_unity(); + for _ in exp..S::S { omega = omega.square(); } @@ -74,11 +73,8 @@ impl> EvaluationDomain { exp, omega, omegainv: omega.invert().unwrap(), - geninv: E::Fr::multiplicative_generator().invert().unwrap(), - minv: E::Fr::from_str(&format!("{}", m)) - .unwrap() - .invert() - .unwrap(), + geninv: S::multiplicative_generator().invert().unwrap(), + minv: S::from_str(&format!("{}", m)).unwrap().invert().unwrap(), }) } @@ -102,7 +98,7 @@ impl> EvaluationDomain { }); } - pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) { + pub fn distribute_powers(&mut self, worker: &Worker, g: S) { worker.scope(self.coeffs.len(), |scope, chunk| { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { scope.spawn(move |_scope| { @@ -117,7 +113,7 @@ impl> EvaluationDomain { } pub fn coset_fft(&mut self, worker: &Worker) { - self.distribute_powers(worker, E::Fr::multiplicative_generator()); + self.distribute_powers(worker, S::multiplicative_generator()); self.fft(worker); } @@ -130,9 +126,9 @@ impl> EvaluationDomain { /// This evaluates t(tau) for this domain, which is /// tau^m - 1 for these radix-2 domains. - pub fn z(&self, tau: &E::Fr) -> E::Fr { + pub fn z(&self, tau: &S) -> S { let mut tmp = tau.pow_vartime(&[self.coeffs.len() as u64]); - tmp.sub_assign(&E::Fr::one()); + tmp.sub_assign(&S::one()); tmp } @@ -141,7 +137,7 @@ impl> EvaluationDomain { /// evaluation domain, so we must perform division over /// a coset. pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { - let i = self.z(&E::Fr::multiplicative_generator()).invert().unwrap(); + let i = self.z(&S::multiplicative_generator()).invert().unwrap(); worker.scope(self.coeffs.len(), |scope, chunk| { for v in self.coeffs.chunks_mut(chunk) { @@ -155,7 +151,7 @@ impl> EvaluationDomain { } /// Perform O(n) multiplication of two polynomials in the domain. - pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain>) { + pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain>) { assert_eq!(self.coeffs.len(), other.coeffs.len()); worker.scope(self.coeffs.len(), |scope, chunk| { @@ -174,7 +170,7 @@ impl> EvaluationDomain { } /// Perform O(n) subtraction of one polynomial from another in the domain. - pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain) { + pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain) { assert_eq!(self.coeffs.len(), other.coeffs.len()); worker.scope(self.coeffs.len(), |scope, chunk| { @@ -193,9 +189,9 @@ impl> EvaluationDomain { } } -pub trait Group: Sized + Copy + Clone + Send + Sync { +pub trait Group: Sized + Copy + Clone + Send + Sync { fn group_zero() -> Self; - fn group_mul_assign(&mut self, by: &E::Fr); + fn group_mul_assign(&mut self, by: &Scalar); fn group_add_assign(&mut self, other: &Self); fn group_sub_assign(&mut self, other: &Self); } @@ -216,7 +212,7 @@ impl Clone for Point { } } -impl> Group for Point { +impl Group for Point { fn group_zero() -> Self { Point(G::identity()) } @@ -231,27 +227,27 @@ impl> Group for Point } } -pub struct Scalar(pub E::Fr); +pub struct Scalar(pub S); -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { +impl PartialEq for Scalar { + fn eq(&self, other: &Scalar) -> bool { self.0 == other.0 } } -impl Copy for Scalar {} +impl Copy for Scalar {} -impl Clone for Scalar { - fn clone(&self) -> Scalar { +impl Clone for Scalar { + fn clone(&self) -> Scalar { *self } } -impl Group for Scalar { +impl Group for Scalar { fn group_zero() -> Self { - Scalar(E::Fr::zero()) + Scalar(S::zero()) } - fn group_mul_assign(&mut self, by: &E::Fr) { + fn group_mul_assign(&mut self, by: &S) { self.0.mul_assign(by); } fn group_add_assign(&mut self, other: &Self) { @@ -262,7 +258,7 @@ impl Group for Scalar { } } -fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) { +fn best_fft>(a: &mut [T], worker: &Worker, omega: &S, log_n: u32) { let log_cpus = worker.log_num_cpus(); if log_n <= log_cpus { @@ -272,7 +268,7 @@ fn best_fft>(a: &mut [T], worker: &Worker, omega: & } } -fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) { +fn serial_fft>(a: &mut [T], omega: &S, log_n: u32) { fn bitreverse(mut n: u32, l: u32) -> u32 { let mut r = 0; for _ in 0..l { @@ -298,7 +294,7 @@ fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u let mut k = 0; while k < n { - let mut w = E::Fr::one(); + let mut w = S::one(); for j in 0..m { let mut t = a[(k + j + m) as usize]; t.group_mul_assign(&w); @@ -316,10 +312,10 @@ fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u } } -fn parallel_fft>( +fn parallel_fft>( a: &mut [T], worker: &Worker, - omega: &E::Fr, + omega: &S, log_n: u32, log_cpus: u32, ) { @@ -339,7 +335,7 @@ fn parallel_fft>( let omega_j = omega.pow_vartime(&[j as u64]); let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]); - let mut elt = E::Fr::one(); + let mut elt = S::one(); for (i, tmp) in tmp.iter_mut().enumerate() { for s in 0..num_cpus { let idx = (i + (s << log_new_n)) % (1 << log_n); @@ -379,23 +375,19 @@ fn parallel_fft>( #[cfg(feature = "pairing")] #[test] fn polynomial_arith() { - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::RngCore; - fn test_mul(rng: &mut R) { + fn test_mul(rng: &mut R) { let worker = Worker::new(); for coeffs_a in 0..70 { for coeffs_b in 0..70 { - let mut a: Vec<_> = (0..coeffs_a) - .map(|_| Scalar::(E::Fr::random(rng))) - .collect(); - let mut b: Vec<_> = (0..coeffs_b) - .map(|_| Scalar::(E::Fr::random(rng))) - .collect(); + let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::(S::random(rng))).collect(); + let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::(S::random(rng))).collect(); // naive evaluation - let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b]; + let mut naive = vec![Scalar(S::zero()); coeffs_a + coeffs_b]; for (i1, a) in a.iter().enumerate() { for (i2, b) in b.iter().enumerate() { let mut prod = *a; @@ -404,8 +396,8 @@ fn polynomial_arith() { } } - a.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero())); - b.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero())); + a.resize(coeffs_a + coeffs_b, Scalar(S::zero())); + b.resize(coeffs_a + coeffs_b, Scalar(S::zero())); let mut a = EvaluationDomain::from_coeffs(a).unwrap(); let mut b = EvaluationDomain::from_coeffs(b).unwrap(); @@ -424,16 +416,16 @@ fn polynomial_arith() { let rng = &mut rand::thread_rng(); - test_mul::(rng); + test_mul::(rng); } #[cfg(feature = "pairing")] #[test] fn fft_composition() { - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::RngCore; - fn test_comp(rng: &mut R) { + fn test_comp(rng: &mut R) { let worker = Worker::new(); for coeffs in 0..10 { @@ -441,7 +433,7 @@ fn fft_composition() { let mut v = vec![]; for _ in 0..coeffs { - v.push(Scalar::(E::Fr::random(rng))); + v.push(Scalar::(S::random(rng))); } let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); @@ -462,17 +454,17 @@ fn fft_composition() { let rng = &mut rand::thread_rng(); - test_comp::(rng); + test_comp::(rng); } #[cfg(feature = "pairing")] #[test] fn parallel_fft_consistency() { - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::RngCore; use std::cmp::min; - fn test_consistency(rng: &mut R) { + fn test_consistency(rng: &mut R) { let worker = Worker::new(); for _ in 0..5 { @@ -480,7 +472,7 @@ fn parallel_fft_consistency() { let d = 1 << log_d; let v1 = (0..d) - .map(|_| Scalar::(E::Fr::random(rng))) + .map(|_| Scalar::(S::random(rng))) .collect::>(); let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); @@ -497,5 +489,5 @@ fn parallel_fft_consistency() { let rng = &mut rand::thread_rng(); - test_consistency::(rng); + test_consistency::(rng); } diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs index 79c42e48b1..226563d427 100644 --- a/bellman/src/gadgets/blake2s.rs +++ b/bellman/src/gadgets/blake2s.rs @@ -4,7 +4,7 @@ use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32}; use crate::{ConstraintSystem, SynthesisError}; -use ff::ScalarEngine; +use ff::PrimeField; /* 2.1. Parameters @@ -79,7 +79,7 @@ const SIGMA: [[usize; 16]; 10] = [ END FUNCTION. */ -fn mixing_g, M>( +fn mixing_g, M>( mut cs: M, v: &mut [UInt32], a: usize, @@ -90,7 +90,7 @@ fn mixing_g, M>( y: &UInt32, ) -> Result<(), SynthesisError> where - M: ConstraintSystem>, + M: ConstraintSystem>, { v[a] = UInt32::addmany( cs.namespace(|| "mixing step 1"), @@ -164,7 +164,7 @@ where END FUNCTION. */ -fn blake2s_compression>( +fn blake2s_compression>( mut cs: CS, h: &mut [UInt32], m: &[UInt32], @@ -337,7 +337,7 @@ fn blake2s_compression>( END FUNCTION. */ -pub fn blake2s>( +pub fn blake2s>( mut cs: CS, input: &[Boolean], personalization: &[u8], @@ -409,7 +409,7 @@ pub fn blake2s>( mod test { use blake2s_simd::Params as Blake2sParams; use hex_literal::hex; - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -420,7 +420,7 @@ mod test { #[test] fn test_blank_hash() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits = vec![]; let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert!(cs.is_satisfied()); @@ -443,7 +443,7 @@ mod test { #[test] fn test_blake2s_constraints() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits: Vec<_> = (0..512) .map(|i| { AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) @@ -461,7 +461,7 @@ mod test { // Test that 512 fixed leading bits (constants) // doesn't result in more constraints. - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -481,7 +481,7 @@ mod test { #[test] fn test_blake2s_constant_constraints() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -512,7 +512,7 @@ mod test { let hash_result = h.finalize(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -559,7 +559,7 @@ mod test { let data: Vec = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); assert_eq!(data.len(), 256); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -596,7 +596,7 @@ mod test { let data: Vec = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); assert_eq!(data.len(), 700); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -651,7 +651,7 @@ mod test { let hash_result = h.finalize(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index b521e7bdfe..940790d9c0 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -1,6 +1,6 @@ //! Gadgets for allocating bits in the circuit and performing boolean logic. -use ff::{BitIterator, Field, PrimeField, ScalarEngine}; +use ff::{BitIterator, PrimeField}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; @@ -26,22 +26,22 @@ impl AllocatedBit { /// Allocate a variable in the constraint system which can only be a /// boolean value. Further, constrain that the boolean is false /// unless the condition is false. - pub fn alloc_conditionally( + pub fn alloc_conditionally( mut cs: CS, value: Option, must_be_false: &AllocatedBit, ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let var = cs.alloc( || "boolean", || { if *value.get()? { - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -67,18 +67,18 @@ impl AllocatedBit { /// Allocate a variable in the constraint system which can only be a /// boolean value. - pub fn alloc(mut cs: CS, value: Option) -> Result + pub fn alloc(mut cs: CS, value: Option) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let var = cs.alloc( || "boolean", || { if *value.get()? { - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -100,10 +100,10 @@ impl AllocatedBit { /// Performs an XOR operation over the two operands, returning /// an `AllocatedBit`. - pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result + pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut result_value = None; @@ -113,11 +113,11 @@ impl AllocatedBit { if *a.value.get()? ^ *b.value.get()? { result_value = Some(true); - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { result_value = Some(false); - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -152,10 +152,10 @@ impl AllocatedBit { /// Performs an AND operation over the two operands, returning /// an `AllocatedBit`. - pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result + pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut result_value = None; @@ -165,11 +165,11 @@ impl AllocatedBit { if *a.value.get()? & *b.value.get()? { result_value = Some(true); - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { result_value = Some(false); - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -190,10 +190,10 @@ impl AllocatedBit { } /// Calculates `a AND (NOT b)`. - pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result + pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut result_value = None; @@ -203,11 +203,11 @@ impl AllocatedBit { if *a.value.get()? & !*b.value.get()? { result_value = Some(true); - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { result_value = Some(false); - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -228,10 +228,10 @@ impl AllocatedBit { } /// Calculates `(NOT a) AND (NOT b)`. - pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result + pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut result_value = None; @@ -241,11 +241,11 @@ impl AllocatedBit { if !*a.value.get()? & !*b.value.get()? { result_value = Some(true); - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { result_value = Some(false); - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } }, )?; @@ -266,7 +266,7 @@ impl AllocatedBit { } } -pub fn u64_into_boolean_vec_le>( +pub fn u64_into_boolean_vec_le>( mut cs: CS, value: Option, ) -> Result, SynthesisError> { @@ -297,16 +297,24 @@ pub fn u64_into_boolean_vec_le>( Ok(bits) } -pub fn field_into_boolean_vec_le, F: PrimeField>( +pub fn field_into_boolean_vec_le< + Scalar: PrimeField, + CS: ConstraintSystem, + F: PrimeField, +>( cs: CS, value: Option, ) -> Result, SynthesisError> { - let v = field_into_allocated_bits_le::(cs, value)?; + let v = field_into_allocated_bits_le::(cs, value)?; Ok(v.into_iter().map(Boolean::from).collect()) } -pub fn field_into_allocated_bits_le, F: PrimeField>( +pub fn field_into_allocated_bits_le< + Scalar: PrimeField, + CS: ConstraintSystem, + F: PrimeField, +>( mut cs: CS, value: Option, ) -> Result, SynthesisError> { @@ -366,10 +374,10 @@ impl Boolean { } } - pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> + pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { match (a, b) { (&Boolean::Constant(a), &Boolean::Constant(b)) => { @@ -384,7 +392,7 @@ impl Boolean { || "enforce equal to one", |lc| lc, |lc| lc, - |lc| lc + CS::one() - &a.lc(CS::one(), E::Fr::one()), + |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::one()), ); Ok(()) @@ -394,7 +402,7 @@ impl Boolean { || "enforce equal to zero", |lc| lc, |lc| lc, - |_| a.lc(CS::one(), E::Fr::one()), + |_| a.lc(CS::one(), Scalar::one()), ); Ok(()) @@ -404,7 +412,7 @@ impl Boolean { || "enforce equal", |lc| lc, |lc| lc, - |_| a.lc(CS::one(), E::Fr::one()) - &b.lc(CS::one(), E::Fr::one()), + |_| a.lc(CS::one(), Scalar::one()) - &b.lc(CS::one(), Scalar::one()), ); Ok(()) @@ -420,18 +428,22 @@ impl Boolean { } } - pub fn lc(&self, one: Variable, coeff: E::Fr) -> LinearCombination { + pub fn lc( + &self, + one: Variable, + coeff: Scalar, + ) -> LinearCombination { match *self { Boolean::Constant(c) => { if c { - LinearCombination::::zero() + (coeff, one) + LinearCombination::::zero() + (coeff, one) } else { - LinearCombination::::zero() + LinearCombination::::zero() } } - Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), + Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), Boolean::Not(ref v) => { - LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) + LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) } } } @@ -451,10 +463,10 @@ impl Boolean { } /// Perform XOR over two boolean operands - pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + pub fn xor<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { match (a, b) { (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()), @@ -473,10 +485,10 @@ impl Boolean { } /// Perform AND over two boolean operands - pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + pub fn and<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { match (a, b) { // false AND x is always false @@ -502,15 +514,15 @@ impl Boolean { } /// Computes (a and b) xor ((not a) and c) - pub fn sha256_ch<'a, E, CS>( + pub fn sha256_ch<'a, Scalar, CS>( mut cs: CS, a: &'a Self, b: &'a Self, c: &'a Self, ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { (Some(a), Some(b), Some(c)) => { @@ -589,16 +601,16 @@ impl Boolean { || { ch_value .get() - .map(|v| if *v { E::Fr::one() } else { E::Fr::zero() }) + .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) }, )?; // a(b - c) = ch - c cs.enforce( || "ch computation", - |_| b.lc(CS::one(), E::Fr::one()) - &c.lc(CS::one(), E::Fr::one()), - |_| a.lc(CS::one(), E::Fr::one()), - |lc| lc + ch - &c.lc(CS::one(), E::Fr::one()), + |_| b.lc(CS::one(), Scalar::one()) - &c.lc(CS::one(), Scalar::one()), + |_| a.lc(CS::one(), Scalar::one()), + |lc| lc + ch - &c.lc(CS::one(), Scalar::one()), ); Ok(AllocatedBit { @@ -609,15 +621,15 @@ impl Boolean { } /// Computes (a and b) xor (a and c) xor (b and c) - pub fn sha256_maj<'a, E, CS>( + pub fn sha256_maj<'a, Scalar, CS>( mut cs: CS, a: &'a Self, b: &'a Self, c: &'a Self, ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { (Some(a), Some(b), Some(c)) => { @@ -692,7 +704,7 @@ impl Boolean { || { maj_value .get() - .map(|v| if *v { E::Fr::one() } else { E::Fr::zero() }) + .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) }, )?; @@ -710,12 +722,12 @@ impl Boolean { cs.enforce( || "maj computation", |_| { - bc.lc(CS::one(), E::Fr::one()) + &bc.lc(CS::one(), E::Fr::one()) - - &b.lc(CS::one(), E::Fr::one()) - - &c.lc(CS::one(), E::Fr::one()) + bc.lc(CS::one(), Scalar::one()) + &bc.lc(CS::one(), Scalar::one()) + - &b.lc(CS::one(), Scalar::one()) + - &c.lc(CS::one(), Scalar::one()) }, - |_| a.lc(CS::one(), E::Fr::one()), - |_| bc.lc(CS::one(), E::Fr::one()) - maj, + |_| a.lc(CS::one(), Scalar::one()), + |_| bc.lc(CS::one(), Scalar::one()) - maj, ); Ok(AllocatedBit { @@ -738,11 +750,11 @@ mod test { use crate::gadgets::test::*; use crate::ConstraintSystem; use ff::{Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; + use pairing::bls12_381::Fr; #[test] fn test_allocated_bit() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); assert!(cs.get("boolean") == Fr::one()); @@ -758,7 +770,7 @@ mod test { fn test_xor() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); @@ -794,7 +806,7 @@ mod test { fn test_and() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); @@ -830,7 +842,7 @@ mod test { fn test_and_not() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); @@ -866,7 +878,7 @@ mod test { fn test_nor() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); @@ -905,7 +917,7 @@ mod test { for a_neg in [false, true].iter().cloned() { for b_neg in [false, true].iter().cloned() { { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::from( AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), @@ -926,7 +938,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::Constant(a_bool); let mut b = Boolean::from( @@ -945,7 +957,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::from( AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), @@ -964,7 +976,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::Constant(a_bool); let mut b = Boolean::Constant(b_bool); @@ -993,7 +1005,7 @@ mod test { #[test] fn test_boolean_negation() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); @@ -1085,7 +1097,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a; let b; @@ -1294,7 +1306,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a; let b; @@ -1515,7 +1527,7 @@ mod test { #[test] fn test_u64_into_boolean_vec_le() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); @@ -1536,7 +1548,7 @@ mod test { #[test] fn test_field_into_allocated_bits_le() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let r = Fr::from_str( "9147677615426976802526883532204139322118074541891858454835346926874644257775", @@ -1573,7 +1585,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a; let b; @@ -1664,7 +1676,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a; let b; @@ -1745,7 +1757,7 @@ mod test { #[test] fn test_alloc_conditionally() { { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); let value = None; @@ -1761,7 +1773,7 @@ mod test { { // since value is true, b must be false, so it should succeed - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let value = Some(true); let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); @@ -1778,7 +1790,7 @@ mod test { { // since value is true, b must be false, so it should fail - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let value = Some(true); let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); @@ -1793,7 +1805,7 @@ mod test { let value = Some(false); //check with false bit - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) .unwrap(); @@ -1801,7 +1813,7 @@ mod test { assert!(cs.is_satisfied()); //check with true bit - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) .unwrap(); diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs index bde86e2ba9..d7fb301518 100644 --- a/bellman/src/gadgets/lookup.rs +++ b/bellman/src/gadgets/lookup.rs @@ -1,7 +1,6 @@ //! Window table lookup gadgets. -use ff::{Field, ScalarEngine}; -use std::ops::{AddAssign, Neg}; +use ff::PrimeField; use super::boolean::Boolean; use super::num::{AllocatedNum, Num}; @@ -9,9 +8,9 @@ use super::*; use crate::ConstraintSystem; // Synthesize the constants for each base pattern. -fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) +fn synth<'a, Scalar: PrimeField, I>(window_size: usize, constants: I, assignment: &mut [Scalar]) where - I: IntoIterator, + I: IntoIterator, { assert_eq!(assignment.len(), 1 << window_size); @@ -29,13 +28,13 @@ where /// Performs a 3-bit window table lookup. `bits` is in /// little-endian order. -pub fn lookup3_xy( +pub fn lookup3_xy( mut cs: CS, bits: &[Boolean], - coords: &[(E::Fr, E::Fr)], -) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> + coords: &[(Scalar, Scalar)], +) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { assert_eq!(bits.len(), 3); assert_eq!(coords.len(), 8); @@ -69,10 +68,10 @@ where let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?; // Compute the coefficients for the lookup constraints - let mut x_coeffs = [E::Fr::zero(); 8]; - let mut y_coeffs = [E::Fr::zero(); 8]; - synth::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(3, coords.iter().map(|c| &c.1), &mut y_coeffs); + let mut x_coeffs = [Scalar::zero(); 8]; + let mut y_coeffs = [Scalar::zero(); 8]; + synth::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); + synth::(3, coords.iter().map(|c| &c.1), &mut y_coeffs); let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?; @@ -82,17 +81,17 @@ where || "x-coordinate lookup", |lc| { lc + (x_coeffs[0b001], one) - + &bits[1].lc::(one, x_coeffs[0b011]) - + &bits[2].lc::(one, x_coeffs[0b101]) - + &precomp.lc::(one, x_coeffs[0b111]) + + &bits[1].lc::(one, x_coeffs[0b011]) + + &bits[2].lc::(one, x_coeffs[0b101]) + + &precomp.lc::(one, x_coeffs[0b111]) }, - |lc| lc + &bits[0].lc::(one, E::Fr::one()), + |lc| lc + &bits[0].lc::(one, Scalar::one()), |lc| { lc + res_x.get_variable() - (x_coeffs[0b000], one) - - &bits[1].lc::(one, x_coeffs[0b010]) - - &bits[2].lc::(one, x_coeffs[0b100]) - - &precomp.lc::(one, x_coeffs[0b110]) + - &bits[1].lc::(one, x_coeffs[0b010]) + - &bits[2].lc::(one, x_coeffs[0b100]) + - &precomp.lc::(one, x_coeffs[0b110]) }, ); @@ -100,17 +99,17 @@ where || "y-coordinate lookup", |lc| { lc + (y_coeffs[0b001], one) - + &bits[1].lc::(one, y_coeffs[0b011]) - + &bits[2].lc::(one, y_coeffs[0b101]) - + &precomp.lc::(one, y_coeffs[0b111]) + + &bits[1].lc::(one, y_coeffs[0b011]) + + &bits[2].lc::(one, y_coeffs[0b101]) + + &precomp.lc::(one, y_coeffs[0b111]) }, - |lc| lc + &bits[0].lc::(one, E::Fr::one()), + |lc| lc + &bits[0].lc::(one, Scalar::one()), |lc| { lc + res_y.get_variable() - (y_coeffs[0b000], one) - - &bits[1].lc::(one, y_coeffs[0b010]) - - &bits[2].lc::(one, y_coeffs[0b100]) - - &precomp.lc::(one, y_coeffs[0b110]) + - &bits[1].lc::(one, y_coeffs[0b010]) + - &bits[2].lc::(one, y_coeffs[0b100]) + - &precomp.lc::(one, y_coeffs[0b110]) }, ); @@ -119,13 +118,13 @@ where /// Performs a 3-bit window table lookup, where /// one of the bits is a sign bit. -pub fn lookup3_xy_with_conditional_negation( +pub fn lookup3_xy_with_conditional_negation( mut cs: CS, bits: &[Boolean], - coords: &[(E::Fr, E::Fr)], -) -> Result<(Num, Num), SynthesisError> + coords: &[(Scalar, Scalar)], +) -> Result<(Num, Num), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { assert_eq!(bits.len(), 3); assert_eq!(coords.len(), 4); @@ -158,10 +157,10 @@ where let one = CS::one(); // Compute the coefficients for the lookup constraints - let mut x_coeffs = [E::Fr::zero(); 4]; - let mut y_coeffs = [E::Fr::zero(); 4]; - synth::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(2, coords.iter().map(|c| &c.1), &mut y_coeffs); + let mut x_coeffs = [Scalar::zero(); 4]; + let mut y_coeffs = [Scalar::zero(); 4]; + synth::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); + synth::(2, coords.iter().map(|c| &c.1), &mut y_coeffs); let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?; @@ -171,15 +170,15 @@ where .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); - let y_lc = precomp.lc::(one, y_coeffs[0b11]) - + &bits[1].lc::(one, y_coeffs[0b10]) - + &bits[0].lc::(one, y_coeffs[0b01]) + let y_lc = precomp.lc::(one, y_coeffs[0b11]) + + &bits[1].lc::(one, y_coeffs[0b10]) + + &bits[0].lc::(one, y_coeffs[0b01]) + (y_coeffs[0b00], one); cs.enforce( || "y-coordinate lookup", |lc| lc + &y_lc + &y_lc, - |lc| lc + &bits[2].lc::(one, E::Fr::one()), + |lc| lc + &bits[2].lc::(one, Scalar::one()), |lc| lc + &y_lc - y.get_variable(), ); @@ -191,9 +190,12 @@ mod test { use super::*; use crate::gadgets::boolean::{AllocatedBit, Boolean}; use crate::gadgets::test::*; - use pairing::bls12_381::{Bls12, Fr}; + + use ff::Field; + use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; + use std::ops::{AddAssign, Neg}; #[test] fn test_lookup3_xy() { @@ -203,7 +205,7 @@ mod test { ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a_val = rng.next_u32() % 2 != 0; let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); @@ -248,7 +250,7 @@ mod test { ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a_val = rng.next_u32() % 2 != 0; let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); @@ -300,7 +302,7 @@ mod test { .map(|_| Fr::random(&mut rng)) .collect(); - synth::(window_size, &constants, &mut assignment); + synth(window_size, &constants, &mut assignment); for b in 0..(1 << window_size) { let mut acc = Fr::zero(); diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs index 37b2d9429e..b2bc151cb9 100644 --- a/bellman/src/gadgets/multieq.rs +++ b/bellman/src/gadgets/multieq.rs @@ -1,16 +1,16 @@ -use ff::{Field, PrimeField, ScalarEngine}; +use ff::PrimeField; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; -pub struct MultiEq> { +pub struct MultiEq> { cs: CS, ops: usize, bits_used: usize, - lhs: LinearCombination, - rhs: LinearCombination, + lhs: LinearCombination, + rhs: LinearCombination, } -impl> MultiEq { +impl> MultiEq { pub fn new(cs: CS) -> Self { MultiEq { cs, @@ -40,17 +40,17 @@ impl> MultiEq { pub fn enforce_equal( &mut self, num_bits: usize, - lhs: &LinearCombination, - rhs: &LinearCombination, + lhs: &LinearCombination, + rhs: &LinearCombination, ) { // Check if we will exceed the capacity - if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) { + if (Scalar::CAPACITY as usize) <= (self.bits_used + num_bits) { self.accumulate(); } - assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits)); + assert!((Scalar::CAPACITY as usize) > (self.bits_used + num_bits)); - let coeff = E::Fr::from_str("2") + let coeff = Scalar::from_str("2") .unwrap() .pow_vartime(&[self.bits_used as u64]); self.lhs = self.lhs.clone() + (coeff, lhs); @@ -59,7 +59,7 @@ impl> MultiEq { } } -impl> Drop for MultiEq { +impl> Drop for MultiEq { fn drop(&mut self) { if self.bits_used > 0 { self.accumulate(); @@ -67,7 +67,9 @@ impl> Drop for MultiEq { } } -impl> ConstraintSystem for MultiEq { +impl> ConstraintSystem + for MultiEq +{ type Root = Self; fn one() -> Variable { @@ -76,7 +78,7 @@ impl> ConstraintSystem for MultiEq(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -85,7 +87,7 @@ impl> ConstraintSystem for MultiEq(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -96,9 +98,9 @@ impl> ConstraintSystem for MultiEq AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { self.cs.enforce(annotation, a, b, c) } diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs index 8ee6a1546d..b4df2eeab1 100644 --- a/bellman/src/gadgets/multipack.rs +++ b/bellman/src/gadgets/multipack.rs @@ -4,19 +4,18 @@ use super::boolean::Boolean; use super::num::Num; use super::Assignment; use crate::{ConstraintSystem, SynthesisError}; -use ff::{Field, PrimeField, ScalarEngine}; -use std::ops::AddAssign; +use ff::PrimeField; /// Takes a sequence of booleans and exposes them as compact /// public inputs -pub fn pack_into_inputs(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> +pub fn pack_into_inputs(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { - for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() { - let mut num = Num::::zero(); - let mut coeff = E::Fr::one(); + for (i, bits) in bits.chunks(Scalar::CAPACITY as usize).enumerate() { + let mut num = Num::::zero(); + let mut coeff = Scalar::one(); for bit in bits { num = num.add_bool_with_coeff(CS::one(), bit, coeff); @@ -28,7 +27,7 @@ where // num * 1 = input cs.enforce( || format!("packing constraint {}", i), - |_| num.lc(E::Fr::one()), + |_| num.lc(Scalar::one()), |lc| lc + CS::one(), |lc| lc + input, ); @@ -51,12 +50,12 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec { .collect() } -pub fn compute_multipacking(bits: &[bool]) -> Vec { +pub fn compute_multipacking(bits: &[bool]) -> Vec { let mut result = vec![]; - for bits in bits.chunks(E::Fr::CAPACITY as usize) { - let mut cur = E::Fr::zero(); - let mut coeff = E::Fr::one(); + for bits in bits.chunks(Scalar::CAPACITY as usize) { + let mut cur = Scalar::zero(); + let mut coeff = Scalar::one(); for bit in bits { if *bit { @@ -75,7 +74,7 @@ pub fn compute_multipacking(bits: &[bool]) -> Vec { #[test] fn test_multipacking() { use crate::ConstraintSystem; - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -88,7 +87,7 @@ fn test_multipacking() { ]); for num_bits in 0..1500 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let bits: Vec = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect(); @@ -102,7 +101,7 @@ fn test_multipacking() { }) .collect::>(); - let expected_inputs = compute_multipacking::(&bits); + let expected_inputs = compute_multipacking(&bits); pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap(); diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index 236689d2b7..7f94d93ea7 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -1,7 +1,6 @@ //! Gadgets representing numbers in the scalar field of the underlying curve. -use ff::{BitIterator, Field, PrimeField, ScalarEngine}; -use std::ops::{AddAssign, MulAssign}; +use ff::{BitIterator, PrimeField}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; @@ -9,12 +8,12 @@ use super::Assignment; use super::boolean::{self, AllocatedBit, Boolean}; -pub struct AllocatedNum { - value: Option, +pub struct AllocatedNum { + value: Option, variable: Variable, } -impl Clone for AllocatedNum { +impl Clone for AllocatedNum { fn clone(&self) -> Self { AllocatedNum { value: self.value, @@ -23,11 +22,11 @@ impl Clone for AllocatedNum { } } -impl AllocatedNum { +impl AllocatedNum { pub fn alloc(mut cs: CS, value: F) -> Result where - CS: ConstraintSystem, - F: FnOnce() -> Result, + CS: ConstraintSystem, + F: FnOnce() -> Result, { let mut new_value = None; let var = cs.alloc( @@ -49,7 +48,7 @@ impl AllocatedNum { pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?; @@ -70,15 +69,15 @@ impl AllocatedNum { /// congruency is not allowed.) pub fn to_bits_le_strict(&self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { - pub fn kary_and( + pub fn kary_and( mut cs: CS, v: &[AllocatedBit], ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert!(!v.is_empty()); @@ -104,7 +103,7 @@ impl AllocatedNum { // We want to ensure that the bit representation of a is // less than or equal to r - 1. let mut a = self.value.map(|e| BitIterator::::new(e.to_repr())); - let b = (-E::Fr::one()).to_repr(); + let b = (-Scalar::one()).to_repr(); let mut result = vec![]; @@ -171,7 +170,7 @@ impl AllocatedNum { // However, now we have to unpack self! let mut lc = LinearCombination::zero(); - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); for bit in result.iter().rev() { lc = lc + (coeff, bit.get_variable()); @@ -192,12 +191,12 @@ impl AllocatedNum { /// "in the field." pub fn to_bits_le(&self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?; let mut lc = LinearCombination::zero(); - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); for bit in bits.iter() { lc = lc + (coeff, bit.get_variable()); @@ -214,7 +213,7 @@ impl AllocatedNum { pub fn mul(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut value = None; @@ -246,7 +245,7 @@ impl AllocatedNum { pub fn square(&self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut value = None; @@ -277,7 +276,7 @@ impl AllocatedNum { pub fn assert_nonzero(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let inv = cs.alloc( || "ephemeral inverse", @@ -315,7 +314,7 @@ impl AllocatedNum { condition: &Boolean, ) -> Result<(Self, Self), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || { if *condition.get_value().get()? { @@ -328,7 +327,7 @@ impl AllocatedNum { cs.enforce( || "first conditional reversal", |lc| lc + a.variable - b.variable, - |_| condition.lc(CS::one(), E::Fr::one()), + |_| condition.lc(CS::one(), Scalar::one()), |lc| lc + a.variable - c.variable, ); @@ -343,14 +342,14 @@ impl AllocatedNum { cs.enforce( || "second conditional reversal", |lc| lc + b.variable - a.variable, - |_| condition.lc(CS::one(), E::Fr::one()), + |_| condition.lc(CS::one(), Scalar::one()), |lc| lc + b.variable - d.variable, ); Ok((c, d)) } - pub fn get_value(&self) -> Option { + pub fn get_value(&self) -> Option { self.value } @@ -359,37 +358,37 @@ impl AllocatedNum { } } -pub struct Num { - value: Option, - lc: LinearCombination, +pub struct Num { + value: Option, + lc: LinearCombination, } -impl From> for Num { - fn from(num: AllocatedNum) -> Num { +impl From> for Num { + fn from(num: AllocatedNum) -> Num { Num { value: num.value, - lc: LinearCombination::::zero() + num.variable, + lc: LinearCombination::::zero() + num.variable, } } } -impl Num { +impl Num { pub fn zero() -> Self { Num { - value: Some(E::Fr::zero()), + value: Some(Scalar::zero()), lc: LinearCombination::zero(), } } - pub fn get_value(&self) -> Option { + pub fn get_value(&self) -> Option { self.value } - pub fn lc(&self, coeff: E::Fr) -> LinearCombination { + pub fn lc(&self, coeff: Scalar) -> LinearCombination { LinearCombination::zero() + (coeff, &self.lc) } - pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self { + pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: Scalar) -> Self { let newval = match (self.value, bit.get_value()) { (Some(mut curval), Some(bval)) => { if bval { @@ -412,7 +411,7 @@ impl Num { mod test { use crate::ConstraintSystem; use ff::{BitIterator, Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; + use pairing::bls12_381::Fr; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Neg, SubAssign}; @@ -422,7 +421,7 @@ mod test { #[test] fn test_allocated_num() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap(); @@ -431,7 +430,7 @@ mod test { #[test] fn test_num_squaring() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); let n2 = n.square(&mut cs).unwrap(); @@ -445,7 +444,7 @@ mod test { #[test] fn test_num_multiplication() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); @@ -467,7 +466,7 @@ mod test { 0xbc, 0xe5, ]); { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); @@ -481,7 +480,7 @@ mod test { } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); @@ -498,7 +497,7 @@ mod test { #[test] fn test_num_nonzero() { { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); n.assert_nonzero(&mut cs).unwrap(); @@ -508,7 +507,7 @@ mod test { assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint")); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap(); assert!(n.assert_nonzero(&mut cs).is_err()); @@ -519,7 +518,7 @@ mod test { fn test_into_bits_strict() { let negone = Fr::one().neg(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); n.to_bits_le_strict(&mut cs).unwrap(); @@ -545,7 +544,7 @@ mod test { for i in 0..200 { let r = Fr::random(&mut rng); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs index 1be898e0d8..2db3e6ce13 100644 --- a/bellman/src/gadgets/sha256.rs +++ b/bellman/src/gadgets/sha256.rs @@ -7,7 +7,7 @@ use super::boolean::Boolean; use super::multieq::MultiEq; use super::uint32::UInt32; use crate::{ConstraintSystem, SynthesisError}; -use ff::ScalarEngine; +use ff::PrimeField; #[allow(clippy::unreadable_literal)] const ROUND_CONSTANTS: [u32; 64] = [ @@ -26,13 +26,13 @@ const IV: [u32; 8] = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]; -pub fn sha256_block_no_padding( +pub fn sha256_block_no_padding( mut cs: CS, input: &[Boolean], ) -> Result, SynthesisError> where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(input.len(), 512); @@ -44,10 +44,10 @@ where ) } -pub fn sha256(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> +pub fn sha256(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert!(input.len() % 8 == 0); @@ -77,14 +77,14 @@ fn get_sha256_iv() -> Vec { IV.iter().map(|&v| UInt32::constant(v)).collect() } -fn sha256_compression_function( +fn sha256_compression_function( cs: CS, input: &[Boolean], current_hash_value: &[UInt32], ) -> Result, SynthesisError> where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(input.len(), 512); assert_eq!(current_hash_value.len(), 8); @@ -128,11 +128,11 @@ where } impl Maybe { - fn compute(self, cs: M, others: &[UInt32]) -> Result + fn compute(self, cs: M, others: &[UInt32]) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, - M: ConstraintSystem>, + Scalar: PrimeField, + CS: ConstraintSystem, + M: ConstraintSystem>, { Ok(match self { Maybe::Concrete(ref v) => return Ok(v.clone()), @@ -274,7 +274,7 @@ mod test { use crate::gadgets::boolean::AllocatedBit; use crate::gadgets::test::TestConstraintSystem; use hex_literal::hex; - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -282,7 +282,7 @@ mod test { fn test_blank_hash() { let iv = get_sha256_iv(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); input_bits[0] = Boolean::Constant(true); let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap(); @@ -312,7 +312,7 @@ mod test { let iv = get_sha256_iv(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits: Vec<_> = (0..512) .map(|i| { Boolean::from( @@ -346,7 +346,7 @@ mod test { h.input(&data); let hash_result = h.result(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; for (byte_i, input_byte) in data.into_iter().enumerate() { diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index be7214ea2a..c1395b10b9 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,12 +1,11 @@ //! Helpers for testing circuit implementations. -use ff::{Endianness, Field, PrimeField, ScalarEngine}; +use ff::{Endianness, PrimeField}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use std::collections::HashMap; use std::fmt::Write; -use std::ops::{AddAssign, MulAssign, Neg}; use byteorder::{BigEndian, ByteOrder}; use std::cmp::Ordering; @@ -22,17 +21,17 @@ enum NamedObject { } /// Constraint system for testing purposes. -pub struct TestConstraintSystem { +pub struct TestConstraintSystem { named_objects: HashMap, current_namespace: Vec, constraints: Vec<( - LinearCombination, - LinearCombination, - LinearCombination, + LinearCombination, + LinearCombination, + LinearCombination, String, )>, - inputs: Vec<(E::Fr, String)>, - aux: Vec<(E::Fr, String)>, + inputs: Vec<(Scalar, String)>, + aux: Vec<(Scalar, String)>, } #[derive(Clone, Copy)] @@ -64,11 +63,11 @@ impl Ord for OrderedVariable { } } -fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap { +fn proc_lc(terms: &[(Variable, Scalar)]) -> BTreeMap { let mut map = BTreeMap::new(); for &(var, coeff) in terms { map.entry(OrderedVariable(var)) - .or_insert_with(E::Fr::zero) + .or_insert_with(Scalar::zero) .add_assign(&coeff); } @@ -87,8 +86,8 @@ fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { - let map = proc_lc::(terms); +fn hash_lc(terms: &[(Variable, Scalar)], h: &mut Blake2sState) { + let map = proc_lc::(terms); let mut buf = [0u8; 9 + 32]; BigEndian::write_u64(&mut buf[0..8], map.len() as u64); @@ -107,7 +106,7 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } let mut coeff_repr = coeff.to_repr(); - ::ReprEndianness::toggle_little_endian(&mut coeff_repr); + ::ReprEndianness::toggle_little_endian(&mut coeff_repr); let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect(); buf[9..].copy_from_slice(&coeff_be[..]); @@ -115,12 +114,12 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } } -fn eval_lc( - terms: &[(Variable, E::Fr)], - inputs: &[(E::Fr, String)], - aux: &[(E::Fr, String)], -) -> E::Fr { - let mut acc = E::Fr::zero(); +fn eval_lc( + terms: &[(Variable, Scalar)], + inputs: &[(Scalar, String)], + aux: &[(Scalar, String)], +) -> Scalar { + let mut acc = Scalar::zero(); for &(var, ref coeff) in terms { let mut tmp = match var.get_unchecked() { @@ -128,26 +127,26 @@ fn eval_lc( Index::Aux(index) => aux[index].0, }; - tmp.mul_assign(&coeff); + tmp.mul_assign(coeff); acc.add_assign(&tmp); } acc } -impl TestConstraintSystem { - pub fn new() -> TestConstraintSystem { +impl TestConstraintSystem { + pub fn new() -> TestConstraintSystem { let mut map = HashMap::new(); map.insert( "ONE".into(), - NamedObject::Var(TestConstraintSystem::::one()), + NamedObject::Var(TestConstraintSystem::::one()), ); TestConstraintSystem { named_objects: map, current_namespace: vec![], constraints: vec![], - inputs: vec![(E::Fr::one(), "ONE".into())], + inputs: vec![(Scalar::one(), "ONE".into())], aux: vec![], } } @@ -155,16 +154,16 @@ impl TestConstraintSystem { pub fn pretty_print(&self) -> String { let mut s = String::new(); - let negone = E::Fr::one().neg(); + let negone = Scalar::one().neg(); - let powers_of_two = (0..E::Fr::NUM_BITS) - .map(|i| E::Fr::from_str("2").unwrap().pow_vartime(&[u64::from(i)])) + let powers_of_two = (0..Scalar::NUM_BITS) + .map(|i| Scalar::from_str("2").unwrap().pow_vartime(&[u64::from(i)])) .collect::>(); - let pp = |s: &mut String, lc: &LinearCombination| { + let pp = |s: &mut String, lc: &LinearCombination| { write!(s, "(").unwrap(); let mut is_first = true; - for (var, coeff) in proc_lc::(lc.as_ref()) { + for (var, coeff) in proc_lc::(lc.as_ref()) { if coeff == negone { write!(s, " - ").unwrap(); } else if !is_first { @@ -172,7 +171,7 @@ impl TestConstraintSystem { } is_first = false; - if coeff != E::Fr::one() && coeff != negone { + if coeff != Scalar::one() && coeff != negone { for (i, x) in powers_of_two.iter().enumerate() { if x == &coeff { write!(s, "2^{} . ", i).unwrap(); @@ -227,9 +226,9 @@ impl TestConstraintSystem { } for constraint in &self.constraints { - hash_lc::(constraint.0.as_ref(), &mut h); - hash_lc::(constraint.1.as_ref(), &mut h); - hash_lc::(constraint.2.as_ref(), &mut h); + hash_lc::(constraint.0.as_ref(), &mut h); + hash_lc::(constraint.1.as_ref(), &mut h); + hash_lc::(constraint.2.as_ref(), &mut h); } let mut s = String::new(); @@ -242,9 +241,9 @@ impl TestConstraintSystem { pub fn which_is_unsatisfied(&self) -> Option<&str> { for &(ref a, ref b, ref c, ref path) in &self.constraints { - let mut a = eval_lc::(a.as_ref(), &self.inputs, &self.aux); - let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); - let c = eval_lc::(c.as_ref(), &self.inputs, &self.aux); + let mut a = eval_lc::(a.as_ref(), &self.inputs, &self.aux); + let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); + let c = eval_lc::(c.as_ref(), &self.inputs, &self.aux); a.mul_assign(&b); @@ -264,7 +263,7 @@ impl TestConstraintSystem { self.constraints.len() } - pub fn set(&mut self, path: &str, to: E::Fr) { + pub fn set(&mut self, path: &str, to: Scalar) { match self.named_objects.get(path) { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { Index::Input(index) => self.inputs[index].0 = to, @@ -278,7 +277,7 @@ impl TestConstraintSystem { } } - pub fn verify(&self, expected: &[E::Fr]) -> bool { + pub fn verify(&self, expected: &[Scalar]) -> bool { assert_eq!(expected.len() + 1, self.inputs.len()); for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) { @@ -294,7 +293,7 @@ impl TestConstraintSystem { self.inputs.len() } - pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr { + pub fn get_input(&mut self, index: usize, path: &str) -> Scalar { let (assignment, name) = self.inputs[index].clone(); assert_eq!(path, name); @@ -302,7 +301,7 @@ impl TestConstraintSystem { assignment } - pub fn get(&mut self, path: &str) -> E::Fr { + pub fn get(&mut self, path: &str) -> Scalar { match self.named_objects.get(path) { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { Index::Input(index) => self.inputs[index].0, @@ -345,12 +344,12 @@ fn compute_path(ns: &[String], this: String) -> String { name } -impl ConstraintSystem for TestConstraintSystem { +impl ConstraintSystem for TestConstraintSystem { type Root = Self; fn alloc(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -365,7 +364,7 @@ impl ConstraintSystem for TestConstraintSystem { fn alloc_input(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -382,9 +381,9 @@ impl ConstraintSystem for TestConstraintSystem { where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { let path = compute_path(&self.current_namespace, annotation().into()); let index = self.constraints.len(); @@ -419,10 +418,10 @@ impl ConstraintSystem for TestConstraintSystem { #[test] fn test_cs() { - use ff::PrimeField; - use pairing::bls12_381::{Bls12, Fr}; + use ff::{Field, PrimeField}; + use pairing::bls12_381::Fr; - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 0); let a = cs @@ -443,7 +442,7 @@ fn test_cs() { cs.set("a/var", Fr::from_str("4").unwrap()); - let one = TestConstraintSystem::::one(); + let one = TestConstraintSystem::::one(); cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); assert!(!cs.is_satisfied()); diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs index 16bb651351..a040a2bd6d 100644 --- a/bellman/src/gadgets/uint32.rs +++ b/bellman/src/gadgets/uint32.rs @@ -3,7 +3,7 @@ //! //! [`sha256`]: crate::gadgets::sha256 -use ff::{Field, PrimeField, ScalarEngine}; +use ff::PrimeField; use crate::{ConstraintSystem, LinearCombination, SynthesisError}; @@ -43,10 +43,10 @@ impl UInt32 { } /// Allocate a `UInt32` in the constraint system - pub fn alloc(mut cs: CS, value: Option) -> Result + pub fn alloc(mut cs: CS, value: Option) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let values = match value { Some(mut val) => { @@ -189,7 +189,7 @@ impl UInt32 { } } - fn triop( + fn triop( mut cs: CS, a: &Self, b: &Self, @@ -198,8 +198,8 @@ impl UInt32 { circuit_fn: U, ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, F: Fn(u32, u32, u32) -> u32, U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, { @@ -225,10 +225,15 @@ impl UInt32 { /// Compute the `maj` value (a and b) xor (a and c) xor (b and c) /// during SHA256. - pub fn sha256_maj(cs: CS, a: &Self, b: &Self, c: &Self) -> Result + pub fn sha256_maj( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { Self::triop( cs, @@ -242,10 +247,15 @@ impl UInt32 { /// Compute the `ch` value `(a and b) xor ((not a) and c)` /// during SHA256. - pub fn sha256_ch(cs: CS, a: &Self, b: &Self, c: &Self) -> Result + pub fn sha256_ch( + cs: CS, + a: &Self, + b: &Self, + c: &Self, + ) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { Self::triop( cs, @@ -258,10 +268,10 @@ impl UInt32 { } /// XOR this `UInt32` with another `UInt32` - pub fn xor(&self, mut cs: CS, other: &Self) -> Result + pub fn xor(&self, mut cs: CS, other: &Self) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let new_value = match (self.value, other.value) { (Some(a), Some(b)) => Some(a ^ b), @@ -283,15 +293,15 @@ impl UInt32 { } /// Perform modular addition of several `UInt32` objects. - pub fn addmany(mut cs: M, operands: &[Self]) -> Result + pub fn addmany(mut cs: M, operands: &[Self]) -> Result where - E: ScalarEngine, - CS: ConstraintSystem, - M: ConstraintSystem>, + Scalar: PrimeField, + CS: ConstraintSystem, + M: ConstraintSystem>, { // Make some arbitrary bounds for ourselves to avoid overflows // in the scalar field - assert!(E::Fr::NUM_BITS >= 64); + assert!(Scalar::NUM_BITS >= 64); assert!(operands.len() >= 2); // Weird trivial cases that should never happen assert!(operands.len() <= 10); @@ -324,7 +334,7 @@ impl UInt32 { // Iterate over each bit of the operand and add the operand to // the linear combination - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); for bit in &op.bits { lc = lc + &bit.lc(CS::one(), coeff); @@ -352,7 +362,7 @@ impl UInt32 { let mut result_lc = LinearCombination::zero(); // Allocate each bit of the result - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); let mut i = 0; while max_value != 0 { // Allocate the bit @@ -392,7 +402,7 @@ mod test { use crate::gadgets::test::*; use crate::ConstraintSystem; use ff::Field; - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -474,7 +484,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -519,7 +529,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -562,7 +572,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -675,7 +685,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -719,7 +729,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 7a0e1b0b9b..060ff1becc 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -2,7 +2,7 @@ use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::Field; +use ff::{Field, PrimeField}; use group::{CurveAffine, CurveProjective, Group, Wnaf}; use pairing::Engine; @@ -22,7 +22,7 @@ pub fn generate_random_parameters( ) -> Result, SynthesisError> where E: Engine, - C: Circuit, + C: Circuit, R: RngCore, { let g1 = E::G1::random(rng); @@ -38,24 +38,24 @@ where /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. -struct KeypairAssembly { +struct KeypairAssembly { num_inputs: usize, num_aux: usize, num_constraints: usize, - at_inputs: Vec>, - bt_inputs: Vec>, - ct_inputs: Vec>, - at_aux: Vec>, - bt_aux: Vec>, - ct_aux: Vec>, + at_inputs: Vec>, + bt_inputs: Vec>, + ct_inputs: Vec>, + at_aux: Vec>, + bt_aux: Vec>, + ct_aux: Vec>, } -impl ConstraintSystem for KeypairAssembly { +impl ConstraintSystem for KeypairAssembly { type Root = Self; fn alloc(&mut self, _: A, _: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -74,7 +74,7 @@ impl ConstraintSystem for KeypairAssembly { fn alloc_input(&mut self, _: A, _: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -95,14 +95,14 @@ impl ConstraintSystem for KeypairAssembly { where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { - fn eval( - l: LinearCombination, - inputs: &mut [Vec<(E::Fr, usize)>], - aux: &mut [Vec<(E::Fr, usize)>], + fn eval( + l: LinearCombination, + inputs: &mut [Vec<(Scalar, usize)>], + aux: &mut [Vec<(Scalar, usize)>], this_constraint: usize, ) { for (index, coeff) in l.0 { @@ -165,7 +165,7 @@ pub fn generate_parameters( ) -> Result, SynthesisError> where E: Engine, - C: Circuit, + C: Circuit, { let mut assembly = KeypairAssembly { num_inputs: 0, @@ -192,7 +192,7 @@ where } // Create bases for blind evaluation of polynomials at tau - let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; + let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?; // Compute G1 window table @@ -302,7 +302,7 @@ where g2_wnaf: &Wnaf>, // Lagrange coefficients for tau - powers_of_tau: &[Scalar], + powers_of_tau: &[Scalar], // QAP polynomials at: &[Vec<(E::Fr, usize)>], @@ -362,11 +362,11 @@ where .zip(bt.iter()) .zip(ct.iter()) { - fn eval_at_tau( - powers_of_tau: &[Scalar], - p: &[(E::Fr, usize)], - ) -> E::Fr { - let mut acc = E::Fr::zero(); + fn eval_at_tau( + powers_of_tau: &[Scalar], + p: &[(S, usize)], + ) -> S { + let mut acc = S::zero(); for &(ref coeff, index) in p { let mut n = powers_of_tau[index].0; @@ -416,7 +416,7 @@ where } // Evaluate for inputs. - eval( + eval::( &g1_wnaf, &g2_wnaf, &powers_of_tau, @@ -434,7 +434,7 @@ where ); // Evaluate for auxiliary variables. - eval( + eval::( &g1_wnaf, &g2_wnaf, &powers_of_tau, diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index a5d8aa5224..f9fb17d8b4 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -479,20 +479,20 @@ mod test_with_bls12_381 { use super::*; use crate::{Circuit, ConstraintSystem, SynthesisError}; - use ff::Field; + use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr}; use rand::thread_rng; use std::ops::MulAssign; #[test] fn serialization() { - struct MySillyCircuit { - a: Option, - b: Option, + struct MySillyCircuit { + a: Option, + b: Option, } - impl Circuit for MySillyCircuit { - fn synthesize>( + impl Circuit for MySillyCircuit { + fn synthesize>( self, cs: &mut CS, ) -> Result<(), SynthesisError> { diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 0052751e9b..ff91c26758 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use futures::Future; -use ff::Field; +use ff::{Field, PrimeField}; use group::{CurveAffine, CurveProjective}; use pairing::Engine; @@ -18,14 +18,14 @@ use crate::multiexp::{multiexp, DensityTracker, FullDensity}; use crate::multicore::Worker; -fn eval( - lc: &LinearCombination, +fn eval( + lc: &LinearCombination, mut input_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>, - input_assignment: &[E::Fr], - aux_assignment: &[E::Fr], -) -> E::Fr { - let mut acc = E::Fr::zero(); + input_assignment: &[S], + aux_assignment: &[S], +) -> S { + let mut acc = S::zero(); for &(index, coeff) in lc.0.iter() { let mut tmp; @@ -45,7 +45,7 @@ fn eval( } } - if coeff == E::Fr::one() { + if coeff == S::one() { acc.add_assign(&tmp); } else { tmp.mul_assign(&coeff); @@ -56,28 +56,28 @@ fn eval( acc } -struct ProvingAssignment { +struct ProvingAssignment { // Density of queries a_aux_density: DensityTracker, b_input_density: DensityTracker, b_aux_density: DensityTracker, // Evaluations of A, B, C polynomials - a: Vec>, - b: Vec>, - c: Vec>, + a: Vec>, + b: Vec>, + c: Vec>, // Assignments of variables - input_assignment: Vec, - aux_assignment: Vec, + input_assignment: Vec, + aux_assignment: Vec, } -impl ConstraintSystem for ProvingAssignment { +impl ConstraintSystem for ProvingAssignment { type Root = Self; fn alloc(&mut self, _: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -90,7 +90,7 @@ impl ConstraintSystem for ProvingAssignment { fn alloc_input(&mut self, _: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -104,9 +104,9 @@ impl ConstraintSystem for ProvingAssignment { where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { let a = a(LinearCombination::zero()); let b = b(LinearCombination::zero()); @@ -166,7 +166,7 @@ pub fn create_random_proof>( ) -> Result, SynthesisError> where E: Engine, - C: Circuit, + C: Circuit, R: RngCore, { let r = E::Fr::random(rng); @@ -183,7 +183,7 @@ pub fn create_proof>( ) -> Result, SynthesisError> where E: Engine, - C: Circuit, + C: Circuit, { let mut prover = ProvingAssignment { a_aux_density: DensityTracker::new(), diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index 276738c85d..8038e1721f 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -1,5 +1,4 @@ use ff::{Field, PrimeField}; -use pairing::Engine; mod dummy_engine; use self::dummy_engine::*; @@ -11,22 +10,22 @@ use crate::{Circuit, ConstraintSystem, SynthesisError}; use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; -struct XORDemo { +struct XORDemo { a: Option, b: Option, - _marker: PhantomData, + _marker: PhantomData, } -impl Circuit for XORDemo { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for XORDemo { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { let a_var = cs.alloc( || "a", || { if self.a.is_some() { if self.a.unwrap() { - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } } else { Err(SynthesisError::AssignmentMissing) @@ -46,9 +45,9 @@ impl Circuit for XORDemo { || { if self.b.is_some() { if self.b.unwrap() { - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } } else { Err(SynthesisError::AssignmentMissing) @@ -68,9 +67,9 @@ impl Circuit for XORDemo { || { if self.a.is_some() && self.b.is_some() { if self.a.unwrap() ^ self.b.unwrap() { - Ok(E::Fr::one()) + Ok(Scalar::one()) } else { - Ok(E::Fr::zero()) + Ok(Scalar::zero()) } } else { Err(SynthesisError::AssignmentMissing) @@ -100,13 +99,13 @@ fn test_xordemo() { let tau = Fr::from_str("3673").unwrap(); let params = { - let c = XORDemo:: { + let c = XORDemo { a: None, b: None, _marker: PhantomData, }; - generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap() + generate_parameters::(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap() }; // This will synthesize the constraint system: diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index 4c6c7eaf83..6412a928bf 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -22,12 +22,13 @@ //! }, //! groth16, Circuit, ConstraintSystem, SynthesisError, //! }; +//! use ff::PrimeField; //! use pairing::{bls12_381::Bls12, Engine}; //! use rand::rngs::OsRng; //! use sha2::{Digest, Sha256}; //! //! /// Our own SHA-256d gadget. Input and output are in little-endian bit order. -//! fn sha256d>( +//! fn sha256d>( //! mut cs: CS, //! data: &[Boolean], //! ) -> Result, SynthesisError> { @@ -57,8 +58,8 @@ //! preimage: Option<[u8; 80]>, //! } //! -//! impl Circuit for MyCircuit { -//! fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +//! impl Circuit for MyCircuit { +//! fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { //! // Compute the values for the bits of the preimage. If we are verifying a proof, //! // we still need to create the same constraints, so we return an equivalent-size //! // Vec of None (indicating that the value of each bit is unknown). @@ -118,7 +119,7 @@ //! //! // Pack the hash as inputs for proof verification. //! let hash_bits = multipack::bytes_to_bits_le(&hash); -//! let inputs = multipack::compute_multipacking::(&hash_bits); +//! let inputs = multipack::compute_multipacking(&hash_bits); //! //! // Check the proof! //! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap()); @@ -142,21 +143,21 @@ pub mod groth16; pub mod multicore; mod multiexp; -use ff::{Field, ScalarEngine}; +use ff::PrimeField; use std::error::Error; use std::fmt; use std::io; use std::marker::PhantomData; -use std::ops::{Add, MulAssign, Neg, Sub}; +use std::ops::{Add, Sub}; /// Computations are expressed in terms of arithmetic circuits, in particular /// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// circuit that can be synthesized. The `synthesize` method is called during /// CRS generation and during proving. -pub trait Circuit { +pub trait Circuit { /// Synthesize the circuit into a rank-1 quadratic constraint system - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError>; + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError>; } /// Represents a variable in our constraint system. @@ -188,59 +189,59 @@ pub enum Index { /// This represents a linear combination of some variables, with coefficients /// in the scalar field of a pairing-friendly elliptic curve group. #[derive(Clone)] -pub struct LinearCombination(Vec<(Variable, E::Fr)>); +pub struct LinearCombination(Vec<(Variable, Scalar)>); -impl AsRef<[(Variable, E::Fr)]> for LinearCombination { - fn as_ref(&self) -> &[(Variable, E::Fr)] { +impl AsRef<[(Variable, Scalar)]> for LinearCombination { + fn as_ref(&self) -> &[(Variable, Scalar)] { &self.0 } } -impl LinearCombination { - pub fn zero() -> LinearCombination { +impl LinearCombination { + pub fn zero() -> LinearCombination { LinearCombination(vec![]) } } -impl Add<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; +impl Add<(Scalar, Variable)> for LinearCombination { + type Output = LinearCombination; - fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { + fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination { self.0.push((var, coeff)); self } } -impl Sub<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; +impl Sub<(Scalar, Variable)> for LinearCombination { + type Output = LinearCombination; #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { + fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination { self + (coeff.neg(), var) } } -impl Add for LinearCombination { - type Output = LinearCombination; +impl Add for LinearCombination { + type Output = LinearCombination; - fn add(self, other: Variable) -> LinearCombination { - self + (E::Fr::one(), other) + fn add(self, other: Variable) -> LinearCombination { + self + (Scalar::one(), other) } } -impl Sub for LinearCombination { - type Output = LinearCombination; +impl Sub for LinearCombination { + type Output = LinearCombination; - fn sub(self, other: Variable) -> LinearCombination { - self - (E::Fr::one(), other) + fn sub(self, other: Variable) -> LinearCombination { + self - (Scalar::one(), other) } } -impl<'a, E: ScalarEngine> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; +impl<'a, Scalar: PrimeField> Add<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { + fn add(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self + (s.1, s.0); } @@ -249,10 +250,10 @@ impl<'a, E: ScalarEngine> Add<&'a LinearCombination> for LinearCombination } } -impl<'a, E: ScalarEngine> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; +impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { + fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self - (s.1, s.0); } @@ -261,10 +262,15 @@ impl<'a, E: ScalarEngine> Sub<&'a LinearCombination> for LinearCombination } } -impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; +impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination)> + for LinearCombination +{ + type Output = LinearCombination; - fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + fn add( + mut self, + (coeff, other): (Scalar, &'a LinearCombination), + ) -> LinearCombination { for s in &other.0 { let mut tmp = s.1; tmp.mul_assign(&coeff); @@ -275,10 +281,15 @@ impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination)> for LinearCombi } } -impl<'a, E: ScalarEngine> Sub<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; +impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination)> + for LinearCombination +{ + type Output = LinearCombination; - fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + fn sub( + mut self, + (coeff, other): (Scalar, &'a LinearCombination), + ) -> LinearCombination { for s in &other.0 { let mut tmp = s.1; tmp.mul_assign(&coeff); @@ -347,10 +358,10 @@ impl fmt::Display for SynthesisError { /// Represents a constraint system which can have new variables /// allocated and constrains between them formed. -pub trait ConstraintSystem: Sized { +pub trait ConstraintSystem: Sized { /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; + type Root: ConstraintSystem; /// Return the "one" input variable fn one() -> Variable { @@ -363,7 +374,7 @@ pub trait ConstraintSystem: Sized { /// namespace. fn alloc(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; @@ -371,7 +382,7 @@ pub trait ConstraintSystem: Sized { /// determine the assignment of the variable. fn alloc_input(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; @@ -381,9 +392,9 @@ pub trait ConstraintSystem: Sized { where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination; + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination; /// Create a new (sub)namespace and enter into it. Not intended /// for downstream use; use `namespace` instead. @@ -401,7 +412,7 @@ pub trait ConstraintSystem: Sized { fn get_root(&mut self) -> &mut Self::Root; /// Begin a namespace for this constraint system. - fn namespace(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root> + fn namespace(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root> where NR: Into, N: FnOnce() -> NR, @@ -414,9 +425,14 @@ pub trait ConstraintSystem: Sized { /// This is a "namespaced" constraint system which borrows a constraint system (pushing /// a namespace context) and, when dropped, pops out of the namespace context. -pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem>(&'a mut CS, PhantomData); - -impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { +pub struct Namespace<'a, Scalar: PrimeField, CS: ConstraintSystem>( + &'a mut CS, + PhantomData, +); + +impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem + for Namespace<'cs, Scalar, CS> +{ type Root = CS::Root; fn one() -> Variable { @@ -425,7 +441,7 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Name fn alloc(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -434,7 +450,7 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Name fn alloc_input(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -445,9 +461,9 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Name where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { self.0.enforce(annotation, a, b, c) } @@ -473,15 +489,17 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Name } } -impl<'a, E: ScalarEngine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { +impl<'a, Scalar: PrimeField, CS: ConstraintSystem> Drop for Namespace<'a, Scalar, CS> { fn drop(&mut self) { self.get_root().pop_namespace() } } -/// Convenience implementation of ConstraintSystem for mutable references to +/// Convenience implementation of ConstraintSystem for mutable references to /// constraint systems. -impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { +impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem + for &'cs mut CS +{ type Root = CS::Root; fn one() -> Variable { @@ -490,7 +508,7 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for &'cs fn alloc(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -499,7 +517,7 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for &'cs fn alloc_input(&mut self, annotation: A, f: F) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { @@ -510,9 +528,9 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for &'cs where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { (**self).enforce(annotation, a, b, c) } diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs index a1de0f1087..17bd10ad0f 100644 --- a/bellman/tests/mimc.rs +++ b/bellman/tests/mimc.rs @@ -4,10 +4,8 @@ use rand::thread_rng; // For benchmarking use std::time::{Duration, Instant}; -// Bring in some tools for using pairing-friendly curves -use ff::{Field, ScalarEngine}; -use pairing::Engine; -use std::ops::{AddAssign, MulAssign}; +// Bring in some tools for using finite fiels +use ff::{Field, PrimeField}; // We're going to use the BLS12-381 pairing-friendly elliptic curve. use pairing::bls12_381::Bls12; @@ -35,7 +33,7 @@ const MIMC_ROUNDS: usize = 322; /// return xL /// } /// ``` -fn mimc(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr { +fn mimc(mut xl: Scalar, mut xr: Scalar, constants: &[Scalar]) -> Scalar { assert_eq!(constants.len(), MIMC_ROUNDS); for i in 0..MIMC_ROUNDS { @@ -53,17 +51,17 @@ fn mimc(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr { /// This is our demo circuit for proving knowledge of the /// preimage of a MiMC hash invocation. -struct MiMCDemo<'a, E: Engine> { - xl: Option, - xr: Option, - constants: &'a [E::Fr], +struct MiMCDemo<'a, Scalar: PrimeField> { + xl: Option, + xr: Option, + constants: &'a [Scalar], } /// Our demo circuit implements this `Circuit` trait which /// is used during paramgen and proving in order to /// synthesize the constraint system. -impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl<'a, Scalar: PrimeField> Circuit for MiMCDemo<'a, Scalar> { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { assert_eq!(self.constants.len(), MIMC_ROUNDS); // Allocate the first component of the preimage. @@ -147,6 +145,8 @@ impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { #[test] fn test_mimc() { + use ff::ScalarEngine; + // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); @@ -160,13 +160,13 @@ fn test_mimc() { // Create parameters for our circuit let params = { - let c = MiMCDemo:: { + let c = MiMCDemo { xl: None, xr: None, constants: &constants, }; - generate_random_parameters(c, rng).unwrap() + generate_random_parameters::(c, rng).unwrap() }; // Prepare the verification key (for proof verification) @@ -187,7 +187,7 @@ fn test_mimc() { // Generate a random preimage and compute the image let xl = ::Fr::random(rng); let xr = ::Fr::random(rng); - let image = mimc::(xl, xr, &constants); + let image = mimc(xl, xr, &constants); proof_vec.truncate(0); diff --git a/zcash_proofs/examples/bench.rs b/zcash_proofs/examples/bench.rs index 2f48786857..cbf6b5e099 100644 --- a/zcash_proofs/examples/bench.rs +++ b/zcash_proofs/examples/bench.rs @@ -19,7 +19,7 @@ fn main() { println!("Creating sample parameters..."); let groth_params = generate_random_parameters::( - Spend { + Spend:: { params: jubjub_params, value_commitment: None, proof_generation_key: None, @@ -37,7 +37,7 @@ fn main() { let mut total_time = Duration::new(0, 0); for _ in 0..SAMPLES { - let value_commitment = ValueCommitment { + let value_commitment = ValueCommitment:: { value: 1, randomness: fs::Fs::random(rng), }; diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index d287e1bd9e..1e573da55a 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -18,8 +18,8 @@ use bellman::gadgets::boolean::Boolean; #[derive(Clone)] pub struct EdwardsPoint { - x: AllocatedNum, - y: AllocatedNum, + x: AllocatedNum, + y: AllocatedNum, } /// Perform a fixed-base scalar multiplication with @@ -31,7 +31,7 @@ pub fn fixed_base_multiplication( params: &E::Params, ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, E: JubjubEngine, { // Represents the result of the multiplication @@ -78,11 +78,11 @@ where } impl EdwardsPoint { - pub fn get_x(&self) -> &AllocatedNum { + pub fn get_x(&self) -> &AllocatedNum { &self.x } - pub fn get_y(&self) -> &AllocatedNum { + pub fn get_y(&self) -> &AllocatedNum { &self.y } @@ -92,7 +92,7 @@ impl EdwardsPoint { params: &E::Params, ) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let tmp = self.double(cs.namespace(|| "first doubling"), params)?; let tmp = tmp.double(cs.namespace(|| "second doubling"), params)?; @@ -109,7 +109,7 @@ impl EdwardsPoint { pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { self.x.inputize(cs.namespace(|| "x"))?; self.y.inputize(cs.namespace(|| "y"))?; @@ -120,7 +120,7 @@ impl EdwardsPoint { /// This converts the point into a representation. pub fn repr(&self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut tmp = vec![]; @@ -142,7 +142,7 @@ impl EdwardsPoint { params: &E::Params, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let p = p.map(|p| p.to_xy()); @@ -163,7 +163,7 @@ impl EdwardsPoint { condition: &Boolean, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute x' = self.x if condition, and 0 otherwise let x_prime = AllocatedNum::alloc(cs.namespace(|| "x'"), || { @@ -220,7 +220,7 @@ impl EdwardsPoint { params: &E::Params, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Represents the current "magnitude" of the base // that we're operating over. Starts at self, @@ -267,12 +267,12 @@ impl EdwardsPoint { pub fn interpret( mut cs: CS, - x: &AllocatedNum, - y: &AllocatedNum, + x: &AllocatedNum, + y: &AllocatedNum, params: &E::Params, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // -x^2 + y^2 = 1 + dx^2y^2 @@ -296,7 +296,7 @@ impl EdwardsPoint { pub fn double(&self, mut cs: CS, params: &E::Params) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute T = (x1 + y1) * (x1 + y1) let t = AllocatedNum::alloc(cs.namespace(|| "T"), || { @@ -395,7 +395,7 @@ impl EdwardsPoint { params: &E::Params, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute U = (x1 + y1) * (x2 + y2) let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { @@ -492,8 +492,8 @@ impl EdwardsPoint { } pub struct MontgomeryPoint { - x: Num, - y: Num, + x: Num, + y: Num, } impl MontgomeryPoint { @@ -506,7 +506,7 @@ impl MontgomeryPoint { params: &E::Params, ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute u = (scale*x) / y let u = AllocatedNum::alloc(cs.namespace(|| "u"), || { @@ -558,7 +558,7 @@ impl MontgomeryPoint { /// in Montgomery, does not check that it's /// on the curve. Useful for constants and /// window table lookups. - pub fn interpret_unchecked(x: Num, y: Num) -> Self { + pub fn interpret_unchecked(x: Num, y: Num) -> Self { MontgomeryPoint { x, y } } @@ -571,7 +571,7 @@ impl MontgomeryPoint { params: &E::Params, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute lambda = (y' - y) / (x' - x) let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { @@ -673,7 +673,7 @@ mod test { ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let p = montgomery::Point::::rand(rng, params); let (u, v) = edwards::Point::from_montgomery(&p, params).to_xy(); @@ -682,7 +682,7 @@ mod test { let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || Ok(y)).unwrap(); - let p = MontgomeryPoint::interpret_unchecked(numx.into(), numy.into()); + let p = MontgomeryPoint::::interpret_unchecked(numx.into(), numy.into()); let q = p.into_edwards(&mut cs, params).unwrap(); @@ -713,7 +713,7 @@ mod test { for _ in 0..100 { let p = edwards::Point::::rand(rng, ¶ms); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let q = EdwardsPoint::witness(&mut cs, Some(p.clone()), ¶ms).unwrap(); let p = p.to_xy(); @@ -727,11 +727,11 @@ mod test { let p = edwards::Point::::rand(rng, ¶ms); let (x, y) = p.to_xy(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); - let p = EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + let p = EdwardsPoint::::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); assert!(cs.is_satisfied()); assert_eq!(p.x.get_value().unwrap(), x); @@ -743,11 +743,11 @@ mod test { let x = Fr::random(rng); let y = Fr::random(rng); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); - EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + EdwardsPoint::::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); } @@ -762,7 +762,7 @@ mod test { ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let p = params.generator(FixedGenerators::NoteCommitmentRandomness); let s = Fs::random(rng); @@ -783,7 +783,7 @@ mod test { .map(|v| Boolean::from(v)) .collect::>(); - let q = fixed_base_multiplication( + let q = fixed_base_multiplication::( cs.namespace(|| "multiplication"), FixedGenerators::NoteCommitmentRandomness, &s_bits, @@ -805,7 +805,7 @@ mod test { ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let p = edwards::Point::::rand(rng, params); let s = Fs::random(rng); @@ -817,7 +817,7 @@ mod test { let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p = EdwardsPoint { + let p = EdwardsPoint:: { x: num_x0, y: num_y0, }; @@ -857,7 +857,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let p = edwards::Point::::rand(rng, params); @@ -866,7 +866,7 @@ mod test { let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p = EdwardsPoint { + let p = EdwardsPoint:: { x: num_x0, y: num_y0, }; @@ -933,7 +933,7 @@ mod test { let (x1, y1) = p2.to_xy(); let (x2, y2) = p3.to_xy(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); @@ -941,7 +941,7 @@ mod test { let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); - let p1 = EdwardsPoint { + let p1 = EdwardsPoint:: { x: num_x0, y: num_y0, }; @@ -993,12 +993,12 @@ mod test { let (x0, y0) = p1.to_xy(); let (x1, y1) = p2.to_xy(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p1 = EdwardsPoint { + let p1 = EdwardsPoint:: { x: num_x0, y: num_y0, }; @@ -1047,7 +1047,7 @@ mod test { let (x1, y1) = p2.to_xy().unwrap(); let (x2, y2) = p3.to_xy().unwrap(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); @@ -1055,7 +1055,7 @@ mod test { let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); - let p1 = MontgomeryPoint { + let p1 = MontgomeryPoint:: { x: num_x0.into(), y: num_y0.into(), }; @@ -1092,7 +1092,7 @@ mod test { let params = &JubjubBls12::new(); let check_small_order_from_p = |p: edwards::Point, is_small_order| { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let p = EdwardsPoint::witness(&mut cs, Some(p), params).unwrap(); assert!(cs.is_satisfied()); diff --git a/zcash_proofs/src/circuit/pedersen_hash.rs b/zcash_proofs/src/circuit/pedersen_hash.rs index 18c2aae1cf..43278a75c8 100644 --- a/zcash_proofs/src/circuit/pedersen_hash.rs +++ b/zcash_proofs/src/circuit/pedersen_hash.rs @@ -22,7 +22,7 @@ pub fn pedersen_hash( params: &E::Params, ) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let personalization = get_constant_bools(&personalization); assert_eq!(personalization.len(), 6); @@ -162,7 +162,7 @@ mod test { ] .iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input: Vec = (0..n_bits).map(|_| rng.next_u32() % 2 != 0).collect(); @@ -177,7 +177,7 @@ mod test { }) .collect(); - pedersen_hash( + pedersen_hash::( cs.namespace(|| "pedersen hash"), Personalization::NoteCommitment, &input_bools, @@ -212,7 +212,7 @@ mod test { for _ in 0..5 { let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input_bools: Vec = input .iter() @@ -225,7 +225,7 @@ mod test { }) .collect(); - let res = pedersen_hash( + let res = pedersen_hash::( cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, @@ -278,7 +278,7 @@ mod test { for length in 300..302 { let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input_bools: Vec = input .iter() @@ -291,7 +291,7 @@ mod test { }) .collect(); - let res = pedersen_hash( + let res = pedersen_hash::( cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index fe20e4fde0..7aecb825cc 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -74,7 +74,7 @@ fn expose_value_commitment( ) -> Result, SynthesisError> where E: JubjubEngine, - CS: ConstraintSystem, + CS: ConstraintSystem, { // Booleanize the value into little-endian bit order let value_bits = boolean::u64_into_boolean_vec_le( @@ -83,7 +83,7 @@ where )?; // Compute the note value in the exponent - let value = ecc::fixed_base_multiplication( + let value = ecc::fixed_base_multiplication::( cs.namespace(|| "compute the value in the exponent"), FixedGenerators::ValueCommitmentValue, &value_bits, @@ -99,7 +99,7 @@ where )?; // Compute the randomness in the exponent - let rcv = ecc::fixed_base_multiplication( + let rcv = ecc::fixed_base_multiplication::( cs.namespace(|| "computation of rcv"), FixedGenerators::ValueCommitmentRandomness, &rcv, @@ -115,8 +115,8 @@ where Ok(value_bits) } -impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), @@ -134,7 +134,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?; // Compute the randomness in the exponent - let ar = ecc::fixed_base_multiplication( + let ar = ecc::fixed_base_multiplication::( cs.namespace(|| "computation of randomization for the signing key"), FixedGenerators::SpendingKeyGenerator, &ar, @@ -161,7 +161,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // congruency then that's equivalent. // Compute nk = [nsk] ProvingPublicKey - nk = ecc::fixed_base_multiplication( + nk = ecc::fixed_base_multiplication::( cs.namespace(|| "computation of nk"), FixedGenerators::ProofGenerationKey, &nsk, @@ -266,7 +266,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { ); // Compute the hash of the note contents - let mut cm = pedersen_hash::pedersen_hash( + let mut cm = pedersen_hash::pedersen_hash::( cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, @@ -281,7 +281,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // Compute the note commitment randomness in the exponent - let rcm = ecc::fixed_base_multiplication( + let rcm = ecc::fixed_base_multiplication::( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, &rcm, @@ -342,7 +342,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?); // Compute the new subtree value - cur = pedersen_hash::pedersen_hash( + cur = pedersen_hash::pedersen_hash::( cs.namespace(|| "computation of pedersen hash"), pedersen_hash::Personalization::MerkleTree(i), &preimage, @@ -379,7 +379,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut rho = cm; { // Compute the position in the exponent - let position = ecc::fixed_base_multiplication( + let position = ecc::fixed_base_multiplication::( cs.namespace(|| "g^position"), FixedGenerators::NullifierPosition, &position_bits, @@ -410,8 +410,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { } } -impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { // Let's start to construct our note, which contains // value (big endian) let mut note_contents = vec![]; @@ -494,7 +494,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { ); // Compute the hash of the note contents - let mut cm = pedersen_hash::pedersen_hash( + let mut cm = pedersen_hash::pedersen_hash::( cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, @@ -509,7 +509,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { )?; // Compute the note commitment randomness in the exponent - let rcm = ecc::fixed_base_multiplication( + let rcm = ecc::fixed_base_multiplication::( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, &rcm, @@ -556,7 +556,7 @@ fn test_input_circuit_with_bls12_381() { let tree_depth = 32; for _ in 0..10 { - let value_commitment = ValueCommitment { + let value_commitment = ValueCommitment:: { value: rng.next_u64(), randomness: fs::Fs::random(rng), }; @@ -638,10 +638,10 @@ fn test_input_circuit_with_bls12_381() { let expected_nf = note.nf(&viewing_key, position, params); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); - let expected_nf = multipack::compute_multipacking::(&expected_nf); + let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Spend { params, @@ -732,7 +732,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ]; for i in 0..10 { - let value_commitment = ValueCommitment { + let value_commitment = ValueCommitment:: { value: i, randomness: fs::Fs::from_str(&(1000 * (i + 1)).to_string()).unwrap(), }; @@ -822,10 +822,10 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let expected_nf = note.nf(&viewing_key, position, params); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); - let expected_nf = multipack::compute_multipacking::(&expected_nf); + let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Spend { params: params, @@ -887,7 +887,7 @@ fn test_output_circuit_with_bls12_381() { ]); for _ in 0..100 { - let value_commitment = ValueCommitment { + let value_commitment = ValueCommitment:: { value: rng.next_u64(), randomness: fs::Fs::random(rng), }; @@ -921,7 +921,7 @@ fn test_output_circuit_with_bls12_381() { let esk = fs::Fs::random(rng); { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Output { params, diff --git a/zcash_proofs/src/circuit/sprout/commitment.rs b/zcash_proofs/src/circuit/sprout/commitment.rs index fba1217389..367be9caf2 100644 --- a/zcash_proofs/src/circuit/sprout/commitment.rs +++ b/zcash_proofs/src/circuit/sprout/commitment.rs @@ -1,9 +1,9 @@ use bellman::gadgets::boolean::Boolean; use bellman::gadgets::sha256::sha256; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; -pub fn note_comm( +pub fn note_comm( cs: CS, a_pk: &[Boolean], value: &[Boolean], @@ -11,8 +11,8 @@ pub fn note_comm( r: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(a_pk.len(), 256); assert_eq!(value.len(), 64); diff --git a/zcash_proofs/src/circuit/sprout/input.rs b/zcash_proofs/src/circuit/sprout/input.rs index a2726d41a2..b2e0d6ced8 100644 --- a/zcash_proofs/src/circuit/sprout/input.rs +++ b/zcash_proofs/src/circuit/sprout/input.rs @@ -1,7 +1,7 @@ use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::sha256::sha256_block_no_padding; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; use super::commitment::note_comm; use super::prfs::*; @@ -13,7 +13,7 @@ pub struct InputNote { } impl InputNote { - pub fn compute( + pub fn compute( mut cs: CS, a_sk: Option, rho: Option, @@ -25,8 +25,8 @@ impl InputNote { rt: &[Boolean], ) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let a_sk = witness_u252( cs.namespace(|| "a_sk"), @@ -106,7 +106,7 @@ impl InputNote { // if enforce is one, they must be equal cs.enforce( || format!("conditionally enforce correct root for bit {}", i), - |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), + |_| cur.lc(CS::one(), Scalar::one()) - &rt.lc(CS::one(), Scalar::one()), |lc| lc + enforce.get_variable(), |lc| lc, ); @@ -118,15 +118,15 @@ impl InputNote { /// Swaps two 256-bit blobs conditionally, returning the /// 512-bit concatenation. -pub fn conditionally_swap_u256( +pub fn conditionally_swap_u256( mut cs: CS, lhs: &[Boolean], rhs: &[Boolean], condition: &AllocatedBit, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(lhs.len(), 256); assert_eq!(rhs.len(), 256); @@ -155,9 +155,9 @@ where // x = rhs cs.enforce( || "conditional swap for x", - |lc| lc + &rhs.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &rhs.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()), |lc| lc + condition.get_variable(), - |lc| lc + &x.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &x.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()), ); let y = Boolean::from(AllocatedBit::alloc( @@ -171,9 +171,9 @@ where // y - rhs = condition (lhs - rhs) cs.enforce( || "conditional swap for y", - |lc| lc + &lhs.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &lhs.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()), |lc| lc + condition.get_variable(), - |lc| lc + &y.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &y.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()), ); new_lhs.push(x); diff --git a/zcash_proofs/src/circuit/sprout/mod.rs b/zcash_proofs/src/circuit/sprout/mod.rs index 6afe677938..b2653ab24d 100644 --- a/zcash_proofs/src/circuit/sprout/mod.rs +++ b/zcash_proofs/src/circuit/sprout/mod.rs @@ -13,8 +13,7 @@ use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::multipack::pack_into_inputs; use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError}; -use ff::Field; -use pairing::Engine; +use ff::PrimeField; mod commitment; mod input; @@ -55,8 +54,8 @@ pub struct JSOutput { pub r: Option, } -impl Circuit for JoinSplit { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for JoinSplit { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { assert_eq!(self.inputs.len(), 2); assert_eq!(self.outputs.len(), 2); @@ -219,10 +218,10 @@ pub struct NoteValue { } impl NoteValue { - fn new(mut cs: CS, value: Option) -> Result + fn new(mut cs: CS, value: Option) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut values; match value { @@ -262,10 +261,10 @@ impl NoteValue { /// Computes this value as a linear combination of /// its bits. - fn lc(&self) -> LinearCombination { + fn lc(&self) -> LinearCombination { let mut tmp = LinearCombination::zero(); - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); for b in &self.bits { tmp = tmp + (coeff, b.get_variable()); coeff = coeff.double(); @@ -281,15 +280,15 @@ impl NoteValue { /// Witnesses some bytes in the constraint system, /// skipping the first `skip_bits`. -fn witness_bits( +fn witness_bits( mut cs: CS, value: Option<&[u8]>, num_bits: usize, skip_bits: usize, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let bit_values = if let Some(value) = value { let mut tmp = vec![]; @@ -318,18 +317,18 @@ where Ok(bits) } -fn witness_u256(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> +fn witness_u256(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { witness_bits(cs, value, 256, 0) } -fn witness_u252(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> +fn witness_u252(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { witness_bits(cs, value, 252, 4) } @@ -338,7 +337,7 @@ where #[ignore] fn test_sprout_constraints() { use bellman::gadgets::test::*; - use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -356,7 +355,7 @@ fn test_sprout_constraints() { } while test_vector.len() != 0 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let phi = Some(get_u256(&mut test_vector)); let rt = Some(get_u256(&mut test_vector)); @@ -462,7 +461,7 @@ fn test_sprout_constraints() { use bellman::gadgets::multipack; let expected_inputs = multipack::bytes_to_bits(&expected_inputs); - let expected_inputs = multipack::compute_multipacking::(&expected_inputs); + let expected_inputs = multipack::compute_multipacking(&expected_inputs); assert!(cs.verify(&expected_inputs)); } diff --git a/zcash_proofs/src/circuit/sprout/output.rs b/zcash_proofs/src/circuit/sprout/output.rs index 73a98514ac..6360f41223 100644 --- a/zcash_proofs/src/circuit/sprout/output.rs +++ b/zcash_proofs/src/circuit/sprout/output.rs @@ -1,6 +1,6 @@ use bellman::gadgets::boolean::Boolean; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; use super::commitment::note_comm; use super::prfs::*; @@ -11,7 +11,7 @@ pub struct OutputNote { } impl OutputNote { - pub fn compute( + pub fn compute( mut cs: CS, a_pk: Option, value: &NoteValue, @@ -21,8 +21,8 @@ impl OutputNote { nonce: bool, ) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?; diff --git a/zcash_proofs/src/circuit/sprout/prfs.rs b/zcash_proofs/src/circuit/sprout/prfs.rs index ea87b08d45..240538a9fe 100644 --- a/zcash_proofs/src/circuit/sprout/prfs.rs +++ b/zcash_proofs/src/circuit/sprout/prfs.rs @@ -1,9 +1,9 @@ use bellman::gadgets::boolean::Boolean; use bellman::gadgets::sha256::sha256_block_no_padding; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; -fn prf( +fn prf( cs: CS, a: bool, b: bool, @@ -13,8 +13,8 @@ fn prf( y: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(x.len(), 252); assert_eq!(y.len(), 256); @@ -32,10 +32,10 @@ where sha256_block_no_padding(cs, &image) } -pub fn prf_a_pk(cs: CS, a_sk: &[Boolean]) -> Result, SynthesisError> +pub fn prf_a_pk(cs: CS, a_sk: &[Boolean]) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf( cs, @@ -50,40 +50,40 @@ where ) } -pub fn prf_nf( +pub fn prf_nf( cs: CS, a_sk: &[Boolean], rho: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, true, true, true, false, a_sk, rho) } -pub fn prf_pk( +pub fn prf_pk( cs: CS, a_sk: &[Boolean], h_sig: &[Boolean], nonce: bool, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, false, nonce, false, false, a_sk, h_sig) } -pub fn prf_rho( +pub fn prf_rho( cs: CS, phi: &[Boolean], h_sig: &[Boolean], nonce: bool, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, false, nonce, true, false, phi, h_sig) } diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index f7b0373433..cc3898e064 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -145,7 +145,7 @@ impl SaplingProvingContext { // Add the nullifier through multiscalar packing { let nullifier = multipack::bytes_to_bits_le(&nullifier); - let nullifier = multipack::compute_multipacking::(&nullifier); + let nullifier = multipack::compute_multipacking(&nullifier); assert_eq!(nullifier.len(), 2); diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index b8869129a9..32e4d8ca86 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -97,7 +97,7 @@ impl SaplingVerificationContext { // Add the nullifier through multiscalar packing { let nullifier = multipack::bytes_to_bits_le(nullifier); - let nullifier = multipack::compute_multipacking::(&nullifier); + let nullifier = multipack::compute_multipacking(&nullifier); assert_eq!(nullifier.len(), 2); diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs index 30ec8217f1..efd0508eca 100644 --- a/zcash_proofs/src/sprout.rs +++ b/zcash_proofs/src/sprout.rs @@ -161,7 +161,7 @@ pub fn verify_proof( public_input.extend(&vpub_new.to_le_bytes()); let public_input = multipack::bytes_to_bits(&public_input); - let public_input = multipack::compute_multipacking::(&public_input); + let public_input = multipack::compute_multipacking(&public_input); let proof = match Proof::read(&proof[..]) { Ok(p) => p, From 0a0e5139480cfff13483f895129796359dbe639d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 15:18:25 +1200 Subject: [PATCH 048/210] ff: Remove ScalarEngine --- bellman/src/groth16/tests/dummy_engine.rs | 7 ++----- bellman/src/multiexp.rs | 14 +++++--------- bellman/tests/mimc.rs | 10 ++++------ ff/src/lib.rs | 8 -------- group/src/lib.rs | 4 ---- pairing/src/bls12_381/mod.rs | 7 ++----- pairing/src/lib.rs | 7 +++++-- 7 files changed, 18 insertions(+), 39 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5686c95c45..bf3bb035a6 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField}; use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; use pairing::{Engine, PairingCurveAffine}; @@ -324,11 +324,8 @@ impl PrimeField for Fr { #[derive(Clone)] pub struct DummyEngine; -impl ScalarEngine for DummyEngine { - type Fr = Fr; -} - impl Engine for DummyEngine { + type Fr = Fr; type G1 = Fr; type G1Affine = Fr; type G2 = Fr; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index a9a868f1ad..c9e00d6ba5 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -293,9 +293,6 @@ where multiexp_inner(pool, bases, density_map, exponents, 0, c, true) } -#[cfg(all(test, feature = "pairing"))] -use ff::ScalarEngine; - #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { @@ -315,17 +312,16 @@ fn test_with_bls12() { } use group::Group; - use pairing::{bls12_381::Bls12, Engine}; + use pairing::{ + bls12_381::{Bls12, Fr}, + Engine, + }; use rand; const SAMPLES: usize = 1 << 14; let rng = &mut rand::thread_rng(); - let v = Arc::new( - (0..SAMPLES) - .map(|_| ::Fr::random(rng)) - .collect::>(), - ); + let v = Arc::new((0..SAMPLES).map(|_| Fr::random(rng)).collect::>()); let g = Arc::new( (0..SAMPLES) .map(|_| ::G1::random(rng).to_affine()) diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs index 17bd10ad0f..b708a18a22 100644 --- a/bellman/tests/mimc.rs +++ b/bellman/tests/mimc.rs @@ -8,7 +8,7 @@ use std::time::{Duration, Instant}; use ff::{Field, PrimeField}; // We're going to use the BLS12-381 pairing-friendly elliptic curve. -use pairing::bls12_381::Bls12; +use pairing::bls12_381::{Bls12, Fr}; // We'll use these interfaces to construct our circuit. use bellman::{Circuit, ConstraintSystem, SynthesisError}; @@ -145,15 +145,13 @@ impl<'a, Scalar: PrimeField> Circuit for MiMCDemo<'a, Scalar> { #[test] fn test_mimc() { - use ff::ScalarEngine; - // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS) - .map(|_| ::Fr::random(rng)) + .map(|_| Fr::random(rng)) .collect::>(); println!("Creating parameters..."); @@ -185,8 +183,8 @@ fn test_mimc() { for _ in 0..SAMPLES { // Generate a random preimage and compute the image - let xl = ::Fr::random(rng); - let xr = ::Fr::random(rng); + let xl = Fr::random(rng); + let xr = Fr::random(rng); let image = mimc(xl, xr, &constants); proof_vec.truncate(0); diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 3382262ee1..e975d91361 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -213,14 +213,6 @@ pub trait PrimeField: Field + From { fn root_of_unity() -> Self; } -/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) -/// with well-defined relationships. Specific relationships (for example, a -/// pairing-friendly curve) can be defined in a subtrait. -pub trait ScalarEngine: Sized + 'static + Clone { - /// This is the scalar field of the engine's groups. - type Fr: PrimeField; -} - #[derive(Debug)] pub struct BitIterator> { t: E, diff --git a/group/src/lib.rs b/group/src/lib.rs index b2e83b30de..4ca6b612e1 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -35,10 +35,6 @@ impl ScalarMul for T where T: Mul: for<'r> ScalarMul<&'r Rhs, Output> {} impl ScalarMulOwned for T where T: for<'r> ScalarMul<&'r Rhs, Output> {} diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index e6aaa063db..2732105c65 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -23,7 +23,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, PairingCurveAffine}; -use ff::{BitIterator, Field, ScalarEngine}; +use ff::{BitIterator, Field}; use group::CurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use subtle::CtOption; @@ -35,11 +35,8 @@ const BLS_X_IS_NEGATIVE: bool = true; #[derive(Clone, Debug)] pub struct Bls12; -impl ScalarEngine for Bls12 { - type Fr = Fr; -} - impl Engine for Bls12 { + type Fr = Fr; type G1 = G1; type G1Affine = G1Affine; type G2 = G2; diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 78394fdd4d..88728d13fd 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -21,14 +21,17 @@ pub mod tests; pub mod bls12_381; use core::ops::Mul; -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Field, PrimeField}; use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are /// of prime order `r`, and are equipped with a bilinear pairing function. -pub trait Engine: ScalarEngine { +pub trait Engine: Sized + 'static + Clone { + /// This is the scalar field of the engine's groups. + type Fr: PrimeField; + /// The projective representation of an element in G1. type G1: CurveProjective + From From 5a40a0fe8f42cfe7e2261bbf93d3a604787c30be Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 09:55:05 +1200 Subject: [PATCH 049/210] group: Remove CurveProjective::Base and CurveAffine::Base These associated types were completly unused. The only place we need information about the base field of an elliptic curve is inside Jubjub when operating over its coordinates to implement EC math inside the circuit, and we can handle that either concretely, or with a future trait specifically for that use-case. --- bellman/src/groth16/tests/dummy_engine.rs | 2 -- group/src/lib.rs | 4 +--- pairing/src/bls12_381/ec.rs | 2 -- pairing/src/lib.rs | 6 ++---- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5686c95c45..2ec5c4eb05 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -394,7 +394,6 @@ impl PrimeGroup for Fr {} impl CurveProjective for Fr { type Affine = Fr; - type Base = Fr; fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { assert_eq!(p.len(), q.len()); @@ -436,7 +435,6 @@ impl CurveAffine for Fr { type Compressed = FakePoint; type Uncompressed = FakePoint; type Projective = Fr; - type Base = Fr; type Scalar = Fr; fn identity() -> Self { diff --git a/group/src/lib.rs b/group/src/lib.rs index b2e83b30de..42fac66304 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -1,7 +1,7 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use ff::{Field, PrimeField}; +use ff::PrimeField; use rand::RngCore; use std::fmt; use std::iter::Sum; @@ -97,7 +97,6 @@ pub trait CurveProjective: + GroupOps<::Affine> + GroupOpsOwned<::Affine> { - type Base: Field; type Affine: CurveAffine + Mul + for<'r> Mul; @@ -136,7 +135,6 @@ pub trait CurveAffine: + for<'r> Mul<::Scalar, Output = ::Projective> { type Scalar: PrimeField; - type Base: Field; type Projective: CurveProjective; type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index b14f04f4fd..b7369901a2 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -200,7 +200,6 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; - type Base = $basefield; type Projective = $projective; type Uncompressed = $uncompressed; type Compressed = $compressed; @@ -748,7 +747,6 @@ macro_rules! curve_impl { impl PrimeGroup for $projective {} impl CurveProjective for $projective { - type Base = $basefield; type Affine = $affine; fn batch_normalize(p: &[Self], q: &mut [$affine]) { diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 78394fdd4d..63a9662f0a 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -30,7 +30,7 @@ use subtle::CtOption; /// of prime order `r`, and are equipped with a bilinear pairing function. pub trait Engine: ScalarEngine { /// The projective representation of an element in G1. - type G1: CurveProjective + type G1: CurveProjective + From + GroupOps + GroupOpsOwned @@ -39,7 +39,6 @@ pub trait Engine: ScalarEngine { /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< - Base = Self::Fq, Scalar = Self::Fr, Projective = Self::G1, Pair = Self::G2Affine, @@ -49,7 +48,7 @@ pub trait Engine: ScalarEngine { + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; /// The projective representation of an element in G2. - type G2: CurveProjective + type G2: CurveProjective + From + GroupOps + GroupOpsOwned @@ -58,7 +57,6 @@ pub trait Engine: ScalarEngine { /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< - Base = Self::Fqe, Scalar = Self::Fr, Projective = Self::G2, Pair = Self::G1Affine, From 534c99327aa95b30036978e6c42007ce4928871f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 09:57:33 +1200 Subject: [PATCH 050/210] pairing: Remove Engine::Fq and Engine::Fqe These are unused now that the Base associated types have been removed from the group traits. --- bellman/src/groth16/tests/dummy_engine.rs | 2 -- pairing/src/bls12_381/mod.rs | 2 -- pairing/src/lib.rs | 6 ------ 3 files changed, 10 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 2ec5c4eb05..948dd03239 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -333,8 +333,6 @@ impl Engine for DummyEngine { type G1Affine = Fr; type G2 = Fr; type G2Affine = Fr; - type Fq = Fr; - type Fqe = Fr; // TODO: This should be F_645131 or something. Doesn't matter for now. type Fqk = Fr; diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index e6aaa063db..20bcd3bd21 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -44,8 +44,6 @@ impl Engine for Bls12 { type G1Affine = G1Affine; type G2 = G2; type G2Affine = G2Affine; - type Fq = Fq; - type Fqe = Fq2; type Fqk = Fq12; fn miller_loop<'a, I>(i: I) -> Self::Fqk diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 63a9662f0a..d720736f4e 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -65,12 +65,6 @@ pub trait Engine: ScalarEngine { + Mul + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; - /// The base field that hosts G1. - type Fq: PrimeField; - - /// The extension field that hosts G2. - type Fqe: Field; - /// The extension field that hosts the target group of the pairing. type Fqk: Field; From c8bf2e9fb7a896df1f5805d52cc596080167dc0f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 16:01:42 +1200 Subject: [PATCH 051/210] pairing: Separate associated types for MillerLoopResult and Gt --- bellman/src/groth16/mod.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 7 ++++--- pairing/src/bls12_381/mod.rs | 5 +++-- pairing/src/lib.rs | 15 +++++++++------ pairing/src/tests/engine.rs | 4 ++-- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index a5d8aa5224..2e0d0a5599 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -400,7 +400,7 @@ impl Parameters { pub struct PreparedVerifyingKey { /// Pairing result of alpha*beta - alpha_g1_beta_g2: E::Fqk, + alpha_g1_beta_g2: E::Gt, /// -gamma in G2 neg_gamma_g2: ::Prepared, /// -delta in G2 diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 948dd03239..8bcb8aa207 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -335,9 +335,10 @@ impl Engine for DummyEngine { type G2Affine = Fr; // TODO: This should be F_645131 or something. Doesn't matter for now. - type Fqk = Fr; + type MillerLoopResult = Fr; + type Gt = Fr; - fn miller_loop<'a, I>(i: I) -> Self::Fqk + fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult where I: IntoIterator< Item = &'a ( @@ -358,7 +359,7 @@ impl Engine for DummyEngine { } /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(this: &Self::Fqk) -> CtOption { + fn final_exponentiation(this: &Self::MillerLoopResult) -> CtOption { CtOption::new(*this, Choice::from(1)) } } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 20bcd3bd21..c47327062e 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -44,9 +44,10 @@ impl Engine for Bls12 { type G1Affine = G1Affine; type G2 = G2; type G2Affine = G2Affine; - type Fqk = Fq12; + type MillerLoopResult = Fq12; + type Gt = Fq12; - fn miller_loop<'a, I>(i: I) -> Self::Fqk + fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult where I: IntoIterator< Item = &'a ( diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index d720736f4e..4c3ce959b7 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -42,7 +42,7 @@ pub trait Engine: ScalarEngine { Scalar = Self::Fr, Projective = Self::G1, Pair = Self::G2Affine, - PairingResult = Self::Fqk, + PairingResult = Self::Gt, > + From + Mul + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; @@ -60,16 +60,19 @@ pub trait Engine: ScalarEngine { Scalar = Self::Fr, Projective = Self::G2, Pair = Self::G1Affine, - PairingResult = Self::Fqk, + PairingResult = Self::Gt, > + From + Mul + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; + /// The type returned by `Engine::miller_loop`. + type MillerLoopResult; + /// The extension field that hosts the target group of the pairing. - type Fqk: Field; + type Gt: Field; /// Perform a miller loop with some number of (G1, G2) pairs. - fn miller_loop<'a, I>(i: I) -> Self::Fqk + fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult where I: IntoIterator< Item = &'a ( @@ -79,10 +82,10 @@ pub trait Engine: ScalarEngine { >; /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(_: &Self::Fqk) -> CtOption; + fn final_exponentiation(_: &Self::MillerLoopResult) -> CtOption; /// Performs a complete pairing operation `(p, q)`. - fn pairing(p: G1, q: G2) -> Self::Fqk + fn pairing(p: G1, q: G2) -> Self::Gt where G1: Into, G2: Into, diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 6af1b801c1..44111c6710 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -30,12 +30,12 @@ pub fn engine_tests() { let d = E::G2::random(&mut rng).to_affine().prepare(); assert_eq!( - E::Fqk::one(), + E::Gt::one(), E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap() ); assert_eq!( - E::Fqk::one(), + E::Gt::one(), E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap() ); From 57bb18ca6f056cfd3072ad764fdcf7a1584675be Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 16:45:44 +1200 Subject: [PATCH 052/210] pairing: Move final_exponentiation into a MillerLoopResult trait --- bellman/src/groth16/tests/dummy_engine.rs | 10 +- bellman/src/groth16/verifier.rs | 8 +- pairing/benches/bls12_381/mod.rs | 4 +- pairing/src/bls12_381/mod.rs | 106 ++++++++++++---------- pairing/src/lib.rs | 27 ++++-- pairing/src/tests/engine.rs | 19 ++-- 6 files changed, 96 insertions(+), 78 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 8bcb8aa207..465bc2d701 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,6 +1,6 @@ use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; -use pairing::{Engine, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; use rand_core::RngCore; use std::fmt; @@ -357,10 +357,14 @@ impl Engine for DummyEngine { acc } +} + +impl MillerLoopResult for Fr { + type Gt = Fr; /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(this: &Self::MillerLoopResult) -> CtOption { - CtOption::new(*this, Choice::from(1)) + fn final_exponentiation(&self) -> Self::Gt { + *self } } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index ae55b14097..d29a4bc9b3 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,5 +1,5 @@ use group::{CurveAffine, CurveProjective}; -use pairing::{Engine, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; @@ -41,14 +41,14 @@ pub fn verify_proof<'a, E: Engine>( // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // which allows us to do a single final exponentiation. - Ok(E::final_exponentiation(&E::miller_loop( + Ok(E::miller_loop( [ (&proof.a.prepare(), &proof.b.prepare()), (&acc.to_affine().prepare(), &pvk.neg_gamma_g2), (&proof.c.prepare(), &pvk.neg_delta_g2), ] .iter(), - )) - .unwrap() + ) + .final_exponentiation() == pvk.alpha_g1_beta_g2) } diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index 212e7baaf8..ad030579c2 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -10,7 +10,7 @@ use rand_xorshift::XorShiftRng; use group::Group; use pairing::bls12_381::*; -use pairing::{Engine, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; fn bench_pairing_g1_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -100,7 +100,7 @@ fn bench_pairing_final_exponentiation(c: &mut Criterion) { let mut count = 0; c.bench_function("Final exponentiation", |b| { b.iter(|| { - let tmp = Bls12::final_exponentiation(&v[count]); + let tmp = v[count].final_exponentiation(); count = (count + 1) % SAMPLES; tmp }) diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index c47327062e..6dd2cb4163 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -21,12 +21,11 @@ pub use self::fq2::Fq2; pub use self::fq6::Fq6; pub use self::fr::{Fr, FrRepr}; -use super::{Engine, PairingCurveAffine}; +use super::{Engine, MillerLoopResult, PairingCurveAffine}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; // The BLS parameter x for BLS12-381 is -0xd201000000010000 const BLS_X: u64 = 0xd201000000010000; @@ -110,59 +109,66 @@ impl Engine for Bls12 { f } +} + +impl MillerLoopResult for Fq12 { + type Gt = Fq12; - fn final_exponentiation(r: &Fq12) -> CtOption { - let mut f1 = *r; + fn final_exponentiation(&self) -> Fq12 { + let mut f1 = *self; f1.conjugate(); - r.invert().map(|mut f2| { - let mut r = f1; - r.mul_assign(&f2); - f2 = r; - r.frobenius_map(2); - r.mul_assign(&f2); - - fn exp_by_x(f: &mut Fq12, x: u64) { - *f = f.pow_vartime(&[x]); - if BLS_X_IS_NEGATIVE { - f.conjugate(); + self.invert() + .map(|mut f2| { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + fn exp_by_x(f: &mut Fq12, x: u64) { + *f = f.pow_vartime(&[x]); + if BLS_X_IS_NEGATIVE { + f.conjugate(); + } } - } - let mut x = BLS_X; - let y0 = r.square(); - let mut y1 = y0; - exp_by_x(&mut y1, x); - x >>= 1; - let mut y2 = y1; - exp_by_x(&mut y2, x); - x <<= 1; - let mut y3 = r; - y3.conjugate(); - y1.mul_assign(&y3); - y1.conjugate(); - y1.mul_assign(&y2); - y2 = y1; - exp_by_x(&mut y2, x); - y3 = y2; - exp_by_x(&mut y3, x); - y1.conjugate(); - y3.mul_assign(&y1); - y1.conjugate(); - y1.frobenius_map(3); - y2.frobenius_map(2); - y1.mul_assign(&y2); - y2 = y3; - exp_by_x(&mut y2, x); - y2.mul_assign(&y0); - y2.mul_assign(&r); - y1.mul_assign(&y2); - y2 = y3; - y2.frobenius_map(1); - y1.mul_assign(&y2); - - y1 - }) + let mut x = BLS_X; + let y0 = r.square(); + let mut y1 = y0; + exp_by_x(&mut y1, x); + x >>= 1; + let mut y2 = y1; + exp_by_x(&mut y2, x); + x <<= 1; + let mut y3 = r; + y3.conjugate(); + y1.mul_assign(&y3); + y1.conjugate(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.conjugate(); + y3.mul_assign(&y1); + y1.conjugate(); + y1.frobenius_map(3); + y2.frobenius_map(2); + y1.mul_assign(&y2); + y2 = y3; + exp_by_x(&mut y2, x); + y2.mul_assign(&y0); + y2.mul_assign(&r); + y1.mul_assign(&y2); + y2 = y3; + y2.frobenius_map(1); + y1.mul_assign(&y2); + + y1 + }) + // self must be nonzero. + .unwrap() } } diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 4c3ce959b7..5248208d11 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,7 +23,6 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; -use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are @@ -66,7 +65,7 @@ pub trait Engine: ScalarEngine { + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; /// The type returned by `Engine::miller_loop`. - type MillerLoopResult; + type MillerLoopResult: MillerLoopResult; /// The extension field that hosts the target group of the pairing. type Gt: Field; @@ -81,19 +80,14 @@ pub trait Engine: ScalarEngine { ), >; - /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(_: &Self::MillerLoopResult) -> CtOption; - /// Performs a complete pairing operation `(p, q)`. fn pairing(p: G1, q: G2) -> Self::Gt where G1: Into, G2: Into, { - Self::final_exponentiation(&Self::miller_loop( - [(&(p.into().prepare()), &(q.into().prepare()))].iter(), - )) - .unwrap() + Self::miller_loop([(&(p.into().prepare()), &(q.into().prepare()))].iter()) + .final_exponentiation() } } @@ -110,3 +104,18 @@ pub trait PairingCurveAffine: CurveAffine { /// Perform a pairing fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; } + +/// Represents results of a Miller loop, one of the most expensive portions of the pairing +/// function. +/// +/// `MillerLoopResult`s cannot be compared with each other until +/// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. +pub trait MillerLoopResult { + /// The extension field that hosts the target group of the pairing. + type Gt: Field; + + /// This performs a "final exponentiation" routine to convert the result of a Miller + /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with + /// other elements of `Gt`. + fn final_exponentiation(&self) -> Self::Gt; +} diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 44111c6710..df2ad8abd1 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -4,7 +4,7 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; -use crate::{Engine, PairingCurveAffine}; +use crate::{Engine, MillerLoopResult, PairingCurveAffine}; pub fn engine_tests() { let mut rng = XorShiftRng::from_seed([ @@ -31,22 +31,22 @@ pub fn engine_tests() { assert_eq!( E::Gt::one(), - E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap() + E::miller_loop(&[(&z1, &b)]).final_exponentiation() ); assert_eq!( E::Gt::one(), - E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap() + E::miller_loop(&[(&a, &z2)]).final_exponentiation() ); assert_eq!( - E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(), - E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap() + E::miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), + E::miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() ); assert_eq!( - E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(), - E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap() + E::miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), + E::miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() ); } @@ -70,7 +70,7 @@ fn random_miller_loop_tests() { let a = a.to_affine().prepare(); let b = b.to_affine().prepare(); - let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap(); + let p1 = E::miller_loop(&[(&a, &b)]).final_exponentiation(); assert_eq!(p1, p2); } @@ -93,8 +93,7 @@ fn random_miller_loop_tests() { let c = c.to_affine().prepare(); let d = d.to_affine().prepare(); - let abcd_with_double_loop = - E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap(); + let abcd_with_double_loop = E::miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); assert_eq!(abcd, abcd_with_double_loop); } From da2e638c7dc3c526600edac4427112bda0cfcc23 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 16:56:58 +1200 Subject: [PATCH 053/210] pairing: Pass affine references to Engine::pairing --- bellman/src/groth16/verifier.rs | 2 +- pairing/benches/bls12_381/mod.rs | 6 ++-- pairing/src/bls12_381/ec.rs | 4 +-- pairing/src/bls12_381/tests/mod.rs | 4 +-- pairing/src/lib.rs | 12 +++---- pairing/src/tests/engine.rs | 57 +++++++++++++----------------- 6 files changed, 37 insertions(+), 48 deletions(-) diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index d29a4bc9b3..6f144fce70 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -11,7 +11,7 @@ pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyi let delta = vk.delta_g2.neg(); PreparedVerifyingKey { - alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), + alpha_g1_beta_g2: E::pairing(&vk.alpha_g1, &vk.beta_g2), neg_gamma_g2: gamma.prepare(), neg_delta_g2: delta.prepare(), ic: vk.ic.clone(), diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index ad030579c2..ba07f2b202 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -115,14 +115,14 @@ fn bench_pairing_full(c: &mut Criterion) { 0xe5, ]); - let v: Vec<(G1, G2)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G2::random(&mut rng))) + let v: Vec<(G1Affine, G2Affine)> = (0..SAMPLES) + .map(|_| (G1::random(&mut rng).into(), G2::random(&mut rng).into())) .collect(); let mut count = 0; c.bench_function("Full pairing", |b| { b.iter(|| { - let tmp = Bls12::pairing(v[count].0, v[count].1); + let tmp = Bls12::pairing(&v[count].0, &v[count].1); count = (count + 1) % SAMPLES; tmp }) diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index b7369901a2..7f6124ad24 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -1141,7 +1141,7 @@ pub mod g1 { } fn perform_pairing(&self, other: &G2Affine) -> Fq12 { - super::super::Bls12::pairing(*self, *other) + super::super::Bls12::pairing(self, other) } } @@ -1780,7 +1780,7 @@ pub mod g2 { } fn perform_pairing(&self, other: &G1Affine) -> Fq12 { - super::super::Bls12::pairing(*other, *self) + super::super::Bls12::pairing(other, self) } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 7b4bbe7aa9..dd7a21bb6b 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, Group}; +use group::{CurveAffine, CurveProjective}; use super::*; use crate::*; @@ -23,7 +23,7 @@ fn test_pairing_result_against_relic() { 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 */ - assert_eq!(Bls12::pairing(G1::generator(), G2::generator()), Fq12 { + assert_eq!(Bls12::pairing(&G1Affine::generator(), &G2Affine::generator()), Fq12 { c0: Fq6 { c0: Fq2 { c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(), diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 5248208d11..d2a8e63afc 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -80,14 +80,10 @@ pub trait Engine: ScalarEngine { ), >; - /// Performs a complete pairing operation `(p, q)`. - fn pairing(p: G1, q: G2) -> Self::Gt - where - G1: Into, - G2: Into, - { - Self::miller_loop([(&(p.into().prepare()), &(q.into().prepare()))].iter()) - .final_exponentiation() + /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and + /// other optimizations. + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + Self::miller_loop([(&(p.prepare()), &(q.prepare()))].iter()).final_exponentiation() } } diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index df2ad8abd1..9968efb0e5 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -17,7 +17,7 @@ pub fn engine_tests() { let b = E::G2::random(&mut rng).to_affine(); assert!(a.pairing_with(&b) == b.pairing_with(&a)); - assert!(a.pairing_with(&b) == E::pairing(a, b)); + assert!(a.pairing_with(&b) == E::pairing(&a, &b)); } for _ in 0..1000 { @@ -62,13 +62,13 @@ fn random_miller_loop_tests() { // Exercise the miller loop for a reduced pairing for _ in 0..1000 { - let a = E::G1::random(&mut rng); - let b = E::G2::random(&mut rng); + let a = E::G1::random(&mut rng).to_affine(); + let b = E::G2::random(&mut rng).to_affine(); - let p2 = E::pairing(a, b); + let p2 = E::pairing(&a, &b); - let a = a.to_affine().prepare(); - let b = b.to_affine().prepare(); + let a = a.prepare(); + let b = b.prepare(); let p1 = E::miller_loop(&[(&a, &b)]).final_exponentiation(); @@ -77,21 +77,21 @@ fn random_miller_loop_tests() { // Exercise a double miller loop for _ in 0..1000 { - let a = E::G1::random(&mut rng); - let b = E::G2::random(&mut rng); - let c = E::G1::random(&mut rng); - let d = E::G2::random(&mut rng); + let a = E::G1::random(&mut rng).to_affine(); + let b = E::G2::random(&mut rng).to_affine(); + let c = E::G1::random(&mut rng).to_affine(); + let d = E::G2::random(&mut rng).to_affine(); - let ab = E::pairing(a, b); - let cd = E::pairing(c, d); + let ab = E::pairing(&a, &b); + let cd = E::pairing(&c, &d); let mut abcd = ab; abcd.mul_assign(&cd); - let a = a.to_affine().prepare(); - let b = b.to_affine().prepare(); - let c = c.to_affine().prepare(); - let d = d.to_affine().prepare(); + let a = a.prepare(); + let b = b.prepare(); + let c = c.prepare(); + let d = d.prepare(); let abcd_with_double_loop = E::miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); @@ -106,26 +106,19 @@ fn random_bilinearity_tests() { ]); for _ in 0..1000 { - let a = E::G1::random(&mut rng); - let b = E::G2::random(&mut rng); + let a = E::G1::random(&mut rng).to_affine(); + let b = E::G2::random(&mut rng).to_affine(); let c = E::Fr::random(&mut rng); let d = E::Fr::random(&mut rng); - let mut ac = a; - MulAssign::<&E::Fr>::mul_assign(&mut ac, &c); - - let mut ad = a; - MulAssign::<&E::Fr>::mul_assign(&mut ad, &d); - - let mut bc = b; - MulAssign::<&E::Fr>::mul_assign(&mut bc, &c); - - let mut bd = b; - MulAssign::<&E::Fr>::mul_assign(&mut bd, &d); + let ac = (a * &c).to_affine(); + let ad = (a * &d).to_affine(); + let bc = (b * &c).to_affine(); + let bd = (b * &d).to_affine(); - let acbd = E::pairing(ac, bd); - let adbc = E::pairing(ad, bc); + let acbd = E::pairing(&ac, &bd); + let adbc = E::pairing(&ad, &bc); let mut cd = (c * &d).to_repr(); ::ReprEndianness::toggle_little_endian(&mut cd); @@ -134,7 +127,7 @@ fn random_bilinearity_tests() { let mut cd_limbs = [0; 4]; byteorder::LittleEndian::read_u64_into(cd.as_ref(), &mut cd_limbs); - let abcd = E::pairing(a, b).pow_vartime(cd_limbs); + let abcd = E::pairing(&a, &b).pow_vartime(cd_limbs); assert_eq!(acbd, adbc); assert_eq!(acbd, abcd); From b9d6df913324d54ce14e64c3db464fee3e1466e2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 18:35:33 +1200 Subject: [PATCH 054/210] pairing: Extract Engine::miller_loop into a MultiMillerLoop trait This enables MultiMillerLoop to be conditionally implemented, for example in libraries where Engine::pairing supports no-std, but MultiMillerLoop requires an allocator. --- bellman/src/groth16/tests/dummy_engine.rs | 29 ++++++++++-------- bellman/src/groth16/verifier.rs | 17 +++++------ pairing/benches/bls12_381/mod.rs | 12 ++++---- pairing/src/bls12_381/mod.rs | 36 +++++++++++++---------- pairing/src/lib.rs | 32 ++++++++++---------- pairing/src/tests/engine.rs | 35 +++++++++++----------- 6 files changed, 84 insertions(+), 77 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 465bc2d701..2a209b1a3a 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,6 +1,6 @@ use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; use std::fmt; @@ -335,21 +335,26 @@ impl Engine for DummyEngine { type G2Affine = Fr; // TODO: This should be F_645131 or something. Doesn't matter for now. - type MillerLoopResult = Fr; type Gt = Fr; - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >, - { + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + } +} + +impl MultiMillerLoop for DummyEngine { + // TODO: This should be F_645131 or something. Doesn't matter for now. + type Result = Fr; + + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result { let mut acc = ::zero(); - for &(a, b) in i { + for &(a, b) in terms { let mut tmp = *a; MulAssign::mul_assign(&mut tmp, b); AddAssign::add_assign(&mut acc, &tmp); diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 6f144fce70..0825f4f889 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,5 +1,5 @@ use group::{CurveAffine, CurveProjective}; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; @@ -18,7 +18,7 @@ pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyi } } -pub fn verify_proof<'a, E: Engine>( +pub fn verify_proof<'a, E: MultiMillerLoop>( pvk: &'a PreparedVerifyingKey, proof: &Proof, public_inputs: &[E::Fr], @@ -41,14 +41,11 @@ pub fn verify_proof<'a, E: Engine>( // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // which allows us to do a single final exponentiation. - Ok(E::miller_loop( - [ - (&proof.a.prepare(), &proof.b.prepare()), - (&acc.to_affine().prepare(), &pvk.neg_gamma_g2), - (&proof.c.prepare(), &pvk.neg_delta_g2), - ] - .iter(), - ) + Ok(E::multi_miller_loop(&[ + (&proof.a, &proof.b.prepare()), + (&acc.to_affine(), &pvk.neg_gamma_g2), + (&proof.c, &pvk.neg_delta_g2), + ]) .final_exponentiation() == pvk.alpha_g1_beta_g2) } diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index ba07f2b202..97326c8cf0 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -10,7 +10,7 @@ use rand_xorshift::XorShiftRng; use group::Group; use pairing::bls12_381::*; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; fn bench_pairing_g1_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -60,10 +60,10 @@ fn bench_pairing_miller_loop(c: &mut Criterion) { 0xe5, ]); - let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) + let v: Vec<(G1Affine, G2Prepared)> = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::random(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)), G2Affine::from(G2::random(&mut rng)).prepare(), ) }) @@ -72,7 +72,7 @@ fn bench_pairing_miller_loop(c: &mut Criterion) { let mut count = 0; c.bench_function("Miller loop", |b| { b.iter(|| { - let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); + let tmp = Bls12::multi_miller_loop(&[(&v[count].0, &v[count].1)]); count = (count + 1) % SAMPLES; tmp }) @@ -90,11 +90,11 @@ fn bench_pairing_final_exponentiation(c: &mut Criterion) { let v: Vec = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::random(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)), G2Affine::from(G2::random(&mut rng)).prepare(), ) }) - .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) + .map(|(ref p, ref q)| Bls12::multi_miller_loop(&[(p, q)])) .collect(); let mut count = 0; diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 6dd2cb4163..3749bb06b7 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -21,7 +21,7 @@ pub use self::fq2::Fq2; pub use self::fq6::Fq6; pub use self::fr::{Fr, FrRepr}; -use super::{Engine, MillerLoopResult, PairingCurveAffine}; +use super::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; @@ -43,21 +43,25 @@ impl Engine for Bls12 { type G1Affine = G1Affine; type G2 = G2; type G2Affine = G2Affine; - type MillerLoopResult = Fq12; type Gt = Fq12; - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >, - { + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + } +} + +impl MultiMillerLoop for Bls12 { + type Result = Fq12; + + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result { let mut pairs = vec![]; - for &(p, q) in i { - if !p.is_identity() && !q.is_identity() { + for &(p, q) in terms { + if !bool::from(p.is_identity()) && !q.is_identity() { pairs.push((p, q.coeffs.iter())); } } @@ -87,12 +91,12 @@ impl Engine for Bls12 { } for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } if i { for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } } @@ -100,7 +104,7 @@ impl Engine for Bls12 { } for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } if BLS_X_IS_NEGATIVE { diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index d2a8e63afc..23bcad32e3 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -64,27 +64,12 @@ pub trait Engine: ScalarEngine { + Mul + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; - /// The type returned by `Engine::miller_loop`. - type MillerLoopResult: MillerLoopResult; - /// The extension field that hosts the target group of the pairing. type Gt: Field; - /// Perform a miller loop with some number of (G1, G2) pairs. - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >; - /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and /// other optimizations. - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::miller_loop([(&(p.prepare()), &(q.prepare()))].iter()).final_exponentiation() - } + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; } /// Affine representation of an elliptic curve point that can be used @@ -101,6 +86,21 @@ pub trait PairingCurveAffine: CurveAffine { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; } +/// An engine that can compute sums of pairings in an efficient way. +pub trait MultiMillerLoop: Engine { + /// The type returned by `Engine::miller_loop`. + type Result: MillerLoopResult; + + /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms + /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result; +} + /// Represents results of a Miller loop, one of the most expensive portions of the pairing /// function. /// diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 9968efb0e5..a688fc64e7 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -4,9 +4,9 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; -use crate::{Engine, MillerLoopResult, PairingCurveAffine}; +use crate::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; -pub fn engine_tests() { +pub fn engine_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -21,32 +21,32 @@ pub fn engine_tests() { } for _ in 0..1000 { - let z1 = E::G1Affine::identity().prepare(); + let z1 = E::G1Affine::identity(); let z2 = E::G2Affine::identity().prepare(); - let a = E::G1::random(&mut rng).to_affine().prepare(); + let a = E::G1::random(&mut rng).to_affine(); let b = E::G2::random(&mut rng).to_affine().prepare(); - let c = E::G1::random(&mut rng).to_affine().prepare(); + let c = E::G1::random(&mut rng).to_affine(); let d = E::G2::random(&mut rng).to_affine().prepare(); assert_eq!( E::Gt::one(), - E::miller_loop(&[(&z1, &b)]).final_exponentiation() + E::multi_miller_loop(&[(&z1, &b)]).final_exponentiation() ); assert_eq!( E::Gt::one(), - E::miller_loop(&[(&a, &z2)]).final_exponentiation() + E::multi_miller_loop(&[(&a, &z2)]).final_exponentiation() ); assert_eq!( - E::miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), - E::miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() + E::multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), + E::multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() ); assert_eq!( - E::miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), - E::miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() + E::multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), + E::multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() ); } @@ -54,7 +54,7 @@ pub fn engine_tests() { random_miller_loop_tests::(); } -fn random_miller_loop_tests() { +fn random_miller_loop_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -67,10 +67,10 @@ fn random_miller_loop_tests() { let p2 = E::pairing(&a, &b); - let a = a.prepare(); + let a = a; let b = b.prepare(); - let p1 = E::miller_loop(&[(&a, &b)]).final_exponentiation(); + let p1 = E::multi_miller_loop(&[(&a, &b)]).final_exponentiation(); assert_eq!(p1, p2); } @@ -88,12 +88,13 @@ fn random_miller_loop_tests() { let mut abcd = ab; abcd.mul_assign(&cd); - let a = a.prepare(); + let a = a; let b = b.prepare(); - let c = c.prepare(); + let c = c; let d = d.prepare(); - let abcd_with_double_loop = E::miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); + let abcd_with_double_loop = + E::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); assert_eq!(abcd, abcd_with_double_loop); } From 03f086221badade44623d0de92b7bba919ac7843 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 3 Jun 2020 18:01:57 +1200 Subject: [PATCH 055/210] pairing: Move PairingCurveAffine::Prepared to MultiMillerLoop trait Prepared elements are only used by MultiMillerLoop, and we don't need the ability to "prepare" G1 elements there. --- bellman/src/groth16/mod.rs | 8 +++---- bellman/src/groth16/tests/dummy_engine.rs | 15 +++---------- bellman/src/groth16/verifier.rs | 10 ++++----- pairing/benches/bls12_381/mod.rs | 27 +++-------------------- pairing/src/bls12_381/ec.rs | 27 +++++------------------ pairing/src/bls12_381/mod.rs | 16 +++++--------- pairing/src/lib.rs | 16 +++++--------- pairing/src/tests/engine.rs | 12 +++++----- 8 files changed, 38 insertions(+), 93 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 2e0d0a5599..ef9425125d 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -3,7 +3,7 @@ //! [Groth16]: https://eprint.iacr.org/2016/260 use group::CurveAffine; -use pairing::{Engine, PairingCurveAffine}; +use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; @@ -398,13 +398,13 @@ impl Parameters { } } -pub struct PreparedVerifyingKey { +pub struct PreparedVerifyingKey { /// Pairing result of alpha*beta alpha_g1_beta_g2: E::Gt, /// -gamma in G2 - neg_gamma_g2: ::Prepared, + neg_gamma_g2: E::G2Prepared, /// -delta in G2 - neg_delta_g2: ::Prepared, + neg_delta_g2: E::G2Prepared, /// Copy of IC from `VerifiyingKey`. ic: Vec, } diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 2a209b1a3a..06063c4d45 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -338,20 +338,16 @@ impl Engine for DummyEngine { type Gt = Fr; fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation() } } impl MultiMillerLoop for DummyEngine { + type G2Prepared = Fr; // TODO: This should be F_645131 or something. Doesn't matter for now. type Result = Fr; - fn multi_miller_loop( - terms: &[( - &Self::G1Affine, - &::Prepared, - )], - ) -> Self::Result { + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { let mut acc = ::zero(); for &(a, b) in terms { @@ -487,14 +483,9 @@ impl CurveAffine for Fr { } impl PairingCurveAffine for Fr { - type Prepared = Fr; type Pair = Fr; type PairingResult = Fr; - fn prepare(&self) -> Self::Prepared { - *self - } - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { self.mul(*other) } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 0825f4f889..e86b15aa52 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,19 +1,19 @@ use group::{CurveAffine, CurveProjective}; -use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; use crate::SynthesisError; -pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { +pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { let gamma = vk.gamma_g2.neg(); let delta = vk.delta_g2.neg(); PreparedVerifyingKey { alpha_g1_beta_g2: E::pairing(&vk.alpha_g1, &vk.beta_g2), - neg_gamma_g2: gamma.prepare(), - neg_delta_g2: delta.prepare(), + neg_gamma_g2: gamma.into(), + neg_delta_g2: delta.into(), ic: vk.ic.clone(), } } @@ -42,7 +42,7 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( // which allows us to do a single final exponentiation. Ok(E::multi_miller_loop(&[ - (&proof.a, &proof.b.prepare()), + (&proof.a, &proof.b.into()), (&acc.to_affine(), &pvk.neg_gamma_g2), (&proof.c, &pvk.neg_delta_g2), ]) diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index 97326c8cf0..7b9b221c55 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -12,26 +12,6 @@ use group::Group; use pairing::bls12_381::*; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; -fn bench_pairing_g1_preparation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("G1 preparation", |b| { - b.iter(|| { - let tmp = G1Affine::from(v[count]).prepare(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - fn bench_pairing_g2_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -45,7 +25,7 @@ fn bench_pairing_g2_preparation(c: &mut Criterion) { let mut count = 0; c.bench_function("G2 preparation", |b| { b.iter(|| { - let tmp = G2Affine::from(v[count]).prepare(); + let tmp = G2Prepared::from(G2Affine::from(v[count])); count = (count + 1) % SAMPLES; tmp }) @@ -64,7 +44,7 @@ fn bench_pairing_miller_loop(c: &mut Criterion) { .map(|_| { ( G1Affine::from(G1::random(&mut rng)), - G2Affine::from(G2::random(&mut rng)).prepare(), + G2Affine::from(G2::random(&mut rng)).into(), ) }) .collect(); @@ -91,7 +71,7 @@ fn bench_pairing_final_exponentiation(c: &mut Criterion) { .map(|_| { ( G1Affine::from(G1::random(&mut rng)), - G2Affine::from(G2::random(&mut rng)).prepare(), + G2Affine::from(G2::random(&mut rng)).into(), ) }) .map(|(ref p, ref q)| Bls12::multi_miller_loop(&[(p, q)])) @@ -131,7 +111,6 @@ fn bench_pairing_full(c: &mut Criterion) { criterion_group!( benches, - bench_pairing_g1_preparation, bench_pairing_g2_preparation, bench_pairing_miller_loop, bench_pairing_final_exponentiation, diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 7f6124ad24..2654afcf64 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -3,7 +3,6 @@ macro_rules! curve_impl { $name:expr, $projective:ident, $affine:ident, - $prepared:ident, $basefield:ident, $scalarfield:ident, $uncompressed:ident, @@ -281,14 +280,9 @@ macro_rules! curve_impl { } impl PairingCurveAffine for $affine { - type Prepared = $prepared; type Pair = $pairing; type PairingResult = Fq12; - fn prepare(&self) -> Self::Prepared { - $prepared::from_affine(*self) - } - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { self.perform_pairing(other) } @@ -917,7 +911,6 @@ pub mod g1 { "G1", G1, G1Affine, - G1Prepared, Fq, Fr, G1Uncompressed, @@ -1173,19 +1166,6 @@ pub mod g1 { } } - #[derive(Clone, Debug)] - pub struct G1Prepared(pub(crate) G1Affine); - - impl G1Prepared { - pub fn is_identity(&self) -> bool { - self.0.is_identity().into() - } - - pub fn from_affine(p: G1Affine) -> Self { - G1Prepared(p) - } - } - #[test] fn g1_generator() { let mut x = Fq::zero(); @@ -1507,7 +1487,6 @@ pub mod g2 { "G2", G2, G2Affine, - G2Prepared, Fq2, Fr, G2Uncompressed, @@ -1818,6 +1797,12 @@ pub mod g2 { pub(crate) infinity: bool, } + impl From for G2Prepared { + fn from(val: G2Affine) -> Self { + Self::from_affine(val) + } + } + #[test] fn g2_generator() { let mut x = Fq2::zero(); diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 3749bb06b7..ad66263ce9 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -12,8 +12,8 @@ mod fr; mod tests; pub use self::ec::{ - G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, - G2Uncompressed, G1, G2, + G1Affine, G1Compressed, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, G2Uncompressed, G1, + G2, }; pub use self::fq::{Fq, FqRepr}; pub use self::fq12::Fq12; @@ -21,7 +21,7 @@ pub use self::fq2::Fq2; pub use self::fq6::Fq6; pub use self::fr::{Fr, FrRepr}; -use super::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use super::{Engine, MillerLoopResult, MultiMillerLoop}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; @@ -46,19 +46,15 @@ impl Engine for Bls12 { type Gt = Fq12; fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation() } } impl MultiMillerLoop for Bls12 { + type G2Prepared = G2Prepared; type Result = Fq12; - fn multi_miller_loop( - terms: &[( - &Self::G1Affine, - &::Prepared, - )], - ) -> Self::Result { + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { let mut pairs = vec![]; for &(p, q) in terms { if !bool::from(p.is_identity()) && !q.is_identity() { diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 23bcad32e3..e2ee969f1d 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -21,7 +21,7 @@ pub mod tests; pub mod bls12_381; use core::ops::Mul; -use ff::{Field, PrimeField, ScalarEngine}; +use ff::{Field, ScalarEngine}; use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -75,30 +75,24 @@ pub trait Engine: ScalarEngine { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. pub trait PairingCurveAffine: CurveAffine { - type Prepared: Clone + Send + Sync + 'static; type Pair: PairingCurveAffine; type PairingResult: Field; - /// Prepares this element for pairing purposes. - fn prepare(&self) -> Self::Prepared; - /// Perform a pairing fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; } /// An engine that can compute sums of pairings in an efficient way. pub trait MultiMillerLoop: Engine { + /// The prepared form of `Self::G2Affine`. + type G2Prepared: Clone + Send + Sync + From; + /// The type returned by `Engine::miller_loop`. type Result: MillerLoopResult; /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ - fn multi_miller_loop( - terms: &[( - &Self::G1Affine, - &::Prepared, - )], - ) -> Self::Result; + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result; } /// Represents results of a Miller loop, one of the most expensive portions of the pairing diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index a688fc64e7..bf0f8849d7 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -22,12 +22,12 @@ pub fn engine_tests() { for _ in 0..1000 { let z1 = E::G1Affine::identity(); - let z2 = E::G2Affine::identity().prepare(); + let z2 = E::G2Affine::identity().into(); let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine().prepare(); + let b = E::G2::random(&mut rng).to_affine().into(); let c = E::G1::random(&mut rng).to_affine(); - let d = E::G2::random(&mut rng).to_affine().prepare(); + let d = E::G2::random(&mut rng).to_affine().into(); assert_eq!( E::Gt::one(), @@ -68,7 +68,7 @@ fn random_miller_loop_tests() { let p2 = E::pairing(&a, &b); let a = a; - let b = b.prepare(); + let b = b.into(); let p1 = E::multi_miller_loop(&[(&a, &b)]).final_exponentiation(); @@ -89,9 +89,9 @@ fn random_miller_loop_tests() { abcd.mul_assign(&cd); let a = a; - let b = b.prepare(); + let b = b.into(); let c = c; - let d = d.prepare(); + let d = d.into(); let abcd_with_double_loop = E::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); From 3779c12e316002b516fb919d4e42cc37c8a4fc36 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 4 Jun 2020 12:24:11 +1200 Subject: [PATCH 056/210] Set activation heights for Heartwood network upgrade --- zcash_primitives/src/consensus.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 8a7385c7e7..a9e9d0692d 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -25,7 +25,7 @@ impl Parameters for MainNetwork { NetworkUpgrade::Overwinter => Some(347_500), NetworkUpgrade::Sapling => Some(419_200), NetworkUpgrade::Blossom => Some(653_600), - NetworkUpgrade::Heartwood => None, + NetworkUpgrade::Heartwood => Some(903_000), } } } @@ -40,7 +40,7 @@ impl Parameters for TestNetwork { NetworkUpgrade::Overwinter => Some(207_500), NetworkUpgrade::Sapling => Some(280_000), NetworkUpgrade::Blossom => Some(584_000), - NetworkUpgrade::Heartwood => None, + NetworkUpgrade::Heartwood => Some(903_800), } } } @@ -231,7 +231,7 @@ mod tests { ); assert_eq!( BranchId::for_height::(5_000_000), - BranchId::Blossom, + BranchId::Heartwood, ); } } From a4b010e0032b71e67330245f34efbe4073e6ce27 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 4 Jun 2020 15:44:15 +0800 Subject: [PATCH 057/210] Placeholders for Canopy network upgrade --- zcash_primitives/src/consensus.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index a9e9d0692d..1b730148d4 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -26,6 +26,7 @@ impl Parameters for MainNetwork { NetworkUpgrade::Sapling => Some(419_200), NetworkUpgrade::Blossom => Some(653_600), NetworkUpgrade::Heartwood => Some(903_000), + NetworkUpgrade::Canopy => None, } } } @@ -41,6 +42,7 @@ impl Parameters for TestNetwork { NetworkUpgrade::Sapling => Some(280_000), NetworkUpgrade::Blossom => Some(584_000), NetworkUpgrade::Heartwood => Some(903_800), + NetworkUpgrade::Canopy => None, } } } @@ -67,6 +69,10 @@ pub enum NetworkUpgrade { /// /// [Heartwood]: https://z.cash/upgrade/heartwood/ Heartwood, + /// The [Canopy] network upgrade. + /// + /// [Canopy]: https://z.cash/upgrade/canopy/ + Canopy, } impl fmt::Display for NetworkUpgrade { @@ -76,6 +82,7 @@ impl fmt::Display for NetworkUpgrade { NetworkUpgrade::Sapling => write!(f, "Sapling"), NetworkUpgrade::Blossom => write!(f, "Blossom"), NetworkUpgrade::Heartwood => write!(f, "Heartwood"), + NetworkUpgrade::Canopy => write!(f, "Canopy"), } } } @@ -87,6 +94,7 @@ impl NetworkUpgrade { NetworkUpgrade::Sapling => BranchId::Sapling, NetworkUpgrade::Blossom => BranchId::Blossom, NetworkUpgrade::Heartwood => BranchId::Heartwood, + NetworkUpgrade::Canopy => BranchId::Canopy, } } } @@ -100,6 +108,7 @@ const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ NetworkUpgrade::Sapling, NetworkUpgrade::Blossom, NetworkUpgrade::Heartwood, + NetworkUpgrade::Canopy, ]; /// A globally-unique identifier for a set of consensus rules within the Zcash chain. @@ -127,6 +136,8 @@ pub enum BranchId { Blossom, /// The consensus rules deployed by [`NetworkUpgrade::Heartwood`]. Heartwood, + /// The consensus rules deployed by [`NetworkUpgrade::Canopy`]. + Canopy, } impl TryFrom for BranchId { @@ -139,6 +150,7 @@ impl TryFrom for BranchId { 0x76b8_09bb => Ok(BranchId::Sapling), 0x2bb4_0e60 => Ok(BranchId::Blossom), 0xf5b9_230b => Ok(BranchId::Heartwood), + 0xe9ff_75a6 => Ok(BranchId::Canopy), _ => Err("Unknown consensus branch ID"), } } @@ -152,6 +164,7 @@ impl From for u32 { BranchId::Sapling => 0x76b8_09bb, BranchId::Blossom => 0x2bb4_0e60, BranchId::Heartwood => 0xf5b9_230b, + BranchId::Canopy => 0xe9ff_75a6, } } } From ae2d2b59b90e0bfd82ae0abe2433b82a884e36c8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 11:48:52 +1200 Subject: [PATCH 058/210] group: Move uncompressed encodings to an UncompressedEncoding trait Specifications of deployed elliptic curves fall into one of two categories: - They specify both compressed and uncompressed encodings, allowing implementations to use either depending on performance vs data size considerations. - They specify a single point encoding format using point compression. I am unaware of any elliptic curve specification that explicitly forbids compressed encodings. To support both categories of elliptic curves, we provide the CurveAffine::Compressed associated type which all curves must define, and then curves that additionally specify an uncompressed encoding may implement the UncompressedEncoding trait and its Uncompressed associated type. pairing::PairingCurveAffine continues to require that its groups provide uncompressed encodings, because this is relied upon by bellman::groth16. We can revisit this restriction when that module is refactored as a separate crate. --- bellman/src/groth16/mod.rs | 10 +++--- bellman/src/groth16/tests/dummy_engine.rs | 7 +++-- group/src/lib.rs | 9 ++++-- group/src/tests/mod.rs | 38 ++++++++++++++++------- pairing/src/bls12_381/ec.rs | 15 ++++++--- pairing/src/bls12_381/tests/mod.rs | 11 ++++--- pairing/src/lib.rs | 7 +++-- 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index e86b4f597c..e4285ce77d 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::CurveAffine; +use group::{CurveAffine, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; @@ -158,7 +158,7 @@ impl VerifyingKey { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Uncompressed::default(); + let mut g1_repr = ::Uncompressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_uncompressed(&g1_repr); @@ -170,7 +170,7 @@ impl VerifyingKey { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Uncompressed::default(); + let mut g2_repr = ::Uncompressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_uncompressed(&g2_repr); @@ -289,7 +289,7 @@ impl Parameters { pub fn read(mut reader: R, checked: bool) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { @@ -317,7 +317,7 @@ impl Parameters { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 2b1c25857d..e9dcb3a8f2 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; +use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -434,7 +434,6 @@ impl AsRef<[u8]> for FakePoint { impl CurveAffine for Fr { type Compressed = FakePoint; - type Uncompressed = FakePoint; type Projective = Fr; type Scalar = Fr; @@ -465,6 +464,10 @@ impl CurveAffine for Fr { fn to_compressed(&self) -> Self::Compressed { unimplemented!() } +} + +impl UncompressedEncoding for Fr { + type Uncompressed = FakePoint; fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { unimplemented!() diff --git a/group/src/lib.rs b/group/src/lib.rs index d88e320c6e..e9b396babd 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -132,7 +132,6 @@ pub trait CurveAffine: { type Scalar: PrimeField; type Projective: CurveProjective; - type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Returns the additive identity. @@ -162,6 +161,12 @@ pub trait CurveAffine: /// Converts this element into its compressed encoding, so long as it's not /// the point at infinity. fn to_compressed(&self) -> Self::Compressed; +} + +/// Affine representation of a point on an elliptic curve that has a defined uncompressed +/// encoding. +pub trait UncompressedEncoding: CurveAffine { + type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its uncompressed encoding. fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; @@ -171,7 +176,7 @@ pub trait CurveAffine: /// /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, /// API invariants may be broken.** Please consider using - /// [`CurveAffine::from_uncompressed`] instead. + /// [`UncompressedEncoding::from_uncompressed`] instead. fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption; /// Converts this element into its uncompressed encoding, so long as it's not diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 406ae6e3b4..1862df8fab 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,7 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective}; +use crate::{CurveAffine, CurveProjective, UncompressedEncoding}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ @@ -62,7 +62,7 @@ pub fn curve_tests() { random_negation_tests::(); random_transformation_tests::(); random_wnaf_tests::(); - random_encoding_tests::(); + random_compressed_encoding_tests::(); } fn random_wnaf_tests() { @@ -394,17 +394,12 @@ fn random_transformation_tests() { } } -fn random_encoding_tests() { +fn random_compressed_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - assert_eq!( - G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), - G::Affine::identity() - ); - assert_eq!( G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(), G::Affine::identity() @@ -413,10 +408,6 @@ fn random_encoding_tests() { for _ in 0..1000 { let mut r = G::random(&mut rng).to_affine(); - let uncompressed = r.to_uncompressed(); - let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); - assert_eq!(de_uncompressed, r); - let compressed = r.to_compressed(); let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); @@ -428,3 +419,26 @@ fn random_encoding_tests() { assert_eq!(de_compressed, r); } } + +pub fn random_uncompressed_encoding_tests() +where + G::Affine: UncompressedEncoding, +{ + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + assert_eq!( + G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), + G::Affine::identity() + ); + + for _ in 0..1000 { + let r = G::random(&mut rng).to_affine(); + + let uncompressed = r.to_uncompressed(); + let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); + assert_eq!(de_uncompressed, r); + } +} diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 2654afcf64..e0d59d1833 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -200,7 +200,6 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; type Projective = $projective; - type Uncompressed = $uncompressed; type Compressed = $compressed; fn identity() -> Self { @@ -248,6 +247,10 @@ macro_rules! curve_impl { fn to_compressed(&self) -> Self::Compressed { $compressed::from_affine(*self) } + } + + impl UncompressedEncoding for $affine { + type Uncompressed = $uncompressed; fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { Self::from_uncompressed_unchecked(bytes).and_then(|affine| { @@ -901,7 +904,7 @@ pub mod g1 { use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1467,8 +1470,9 @@ pub mod g1 { #[test] fn g1_curve_tests() { - use group::tests::curve_tests; + use group::tests::{curve_tests, random_uncompressed_encoding_tests}; curve_tests::(); + random_uncompressed_encoding_tests::(); } } @@ -1477,7 +1481,7 @@ pub mod g2 { use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -2167,8 +2171,9 @@ pub mod g2 { #[test] fn g2_curve_tests() { - use group::tests::curve_tests; + use group::tests::{curve_tests, random_uncompressed_encoding_tests}; curve_tests::(); + random_uncompressed_encoding_tests::(); } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index dd7a21bb6b..30be7edbbf 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CurveProjective, UncompressedEncoding}; use super::*; use crate::*; @@ -55,9 +55,12 @@ fn test_pairing_result_against_relic() { }); } -fn uncompressed_test_vectors(expected: &[u8]) { +fn uncompressed_test_vectors(expected: &[u8]) +where + G::Affine: UncompressedEncoding, +{ let mut e = G::identity(); - let encoded_len = ::Uncompressed::default() + let encoded_len = ::Uncompressed::default() .as_ref() .len(); @@ -69,7 +72,7 @@ fn uncompressed_test_vectors(expected: &[u8]) { let encoded = e_affine.to_uncompressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Uncompressed::default(); + let mut decoded = ::Uncompressed::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; let decoded = G::Affine::from_uncompressed(&decoded).unwrap(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 62af20dc1b..6c3e2b47f8 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -22,7 +22,10 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; +use group::{ + CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, + UncompressedEncoding, +}; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are @@ -77,7 +80,7 @@ pub trait Engine: Sized + 'static + Clone { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. -pub trait PairingCurveAffine: CurveAffine { +pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; type PairingResult: Field; From 3759fc8aabed815edc985f6d0e67894275e0324c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 11:58:57 +1200 Subject: [PATCH 059/210] group: Default implementation of CurveProjective::batch_normalize For convenience. Implementations will usually override this to take advantage of implementation-specific batching optimisations. --- bellman/src/groth16/tests/dummy_engine.rs | 8 -------- group/src/lib.rs | 8 +++++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index e9dcb3a8f2..22361d6b59 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -396,14 +396,6 @@ impl PrimeGroup for Fr {} impl CurveProjective for Fr { type Affine = Fr; - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { - assert_eq!(p.len(), q.len()); - - for (p, q) in p.iter().zip(q.iter_mut()) { - *q = p.to_affine(); - } - } - fn to_affine(&self) -> Fr { *self } diff --git a/group/src/lib.rs b/group/src/lib.rs index e9b396babd..8837345f20 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -99,7 +99,13 @@ pub trait CurveProjective: /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]); + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + assert_eq!(p.len(), q.len()); + + for (p, q) in p.iter().zip(q.iter_mut()) { + *q = p.to_affine(); + } + } /// Converts this element into its affine representation. fn to_affine(&self) -> Self::Affine; From 71586914d471f798250d45c674a54e0fc785e63d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 20:22:53 +1200 Subject: [PATCH 060/210] group: Extract compressed encodings from CurveAffine trait --- bellman/src/groth16/mod.rs | 6 +++--- bellman/src/groth16/tests/dummy_engine.rs | 7 +++++-- group/src/lib.rs | 7 ++++++- group/src/tests/mod.rs | 2 +- pairing/src/bls12_381/ec.rs | 13 ++++++++++--- pairing/src/bls12_381/tests/mod.rs | 6 +++--- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index e4285ce77d..0d85c0ffb5 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{CurveAffine, UncompressedEncoding}; +use group::{CurveAffine, GroupEncoding, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; @@ -47,7 +47,7 @@ impl Proof { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Compressed::default(); + let mut g1_repr = ::Compressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_compressed(&g1_repr); @@ -70,7 +70,7 @@ impl Proof { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Compressed::default(); + let mut g2_repr = ::Compressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_compressed(&g2_repr); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 22361d6b59..5fb661bf4b 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; +use group::{CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -425,7 +425,6 @@ impl AsRef<[u8]> for FakePoint { } impl CurveAffine for Fr { - type Compressed = FakePoint; type Projective = Fr; type Scalar = Fr; @@ -444,6 +443,10 @@ impl CurveAffine for Fr { fn to_projective(&self) -> Self::Projective { *self } +} + +impl GroupEncoding for Fr { + type Compressed = FakePoint; fn from_compressed(_bytes: &Self::Compressed) -> CtOption { unimplemented!() diff --git a/group/src/lib.rs b/group/src/lib.rs index 8837345f20..59e25aa76b 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -132,13 +132,13 @@ pub trait CurveAffine: + PartialEq + Eq + 'static + + GroupEncoding + Neg + Mul<::Scalar, Output = ::Projective> + for<'r> Mul<::Scalar, Output = ::Projective> { type Scalar: PrimeField; type Projective: CurveProjective; - type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Returns the additive identity. fn identity() -> Self; @@ -152,6 +152,11 @@ pub trait CurveAffine: /// Converts this element into its affine representation. fn to_projective(&self) -> Self::Projective; +} + +pub trait GroupEncoding: Sized { + /// The encoding of group elements. + type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its compressed encoding. fn from_compressed(bytes: &Self::Compressed) -> CtOption; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 1862df8fab..cb1f12f8ae 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,7 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective, UncompressedEncoding}; +use crate::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index e0d59d1833..29c591cf26 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -200,7 +200,6 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; type Projective = $projective; - type Compressed = $compressed; fn identity() -> Self { $affine { @@ -221,6 +220,10 @@ macro_rules! curve_impl { fn to_projective(&self) -> $projective { (*self).into() } + } + + impl GroupEncoding for $affine { + type Compressed = $compressed; fn from_compressed(bytes: &Self::Compressed) -> CtOption { Self::from_compressed_unchecked(bytes).and_then(|affine| { @@ -904,7 +907,9 @@ pub mod g1 { use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; + use group::{ + CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1481,7 +1486,9 @@ pub mod g2 { use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; + use group::{ + CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 30be7edbbf..cac563caff 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, UncompressedEncoding}; +use group::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; use super::*; use crate::*; @@ -87,7 +87,7 @@ where fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); - let encoded_len = ::Compressed::default() + let encoded_len = ::Compressed::default() .as_ref() .len(); @@ -99,7 +99,7 @@ fn compressed_test_vectors(expected: &[u8]) { let encoded = e_affine.to_compressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Compressed::default(); + let mut decoded = ::Compressed::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; let decoded = G::Affine::from_compressed(&decoded).unwrap(); From df13cd7480df294aca1f2e69276581774fa353cb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 20:58:54 +1200 Subject: [PATCH 061/210] group: Remove "compressed" notion from GroupEncoding A generic group has a single encoding; for elliptic curves, this happens to be the compressed encoding. --- bellman/src/groth16/mod.rs | 14 +++---- bellman/src/groth16/tests/dummy_engine.rs | 8 ++-- group/src/lib.rs | 20 +++++----- group/src/tests/mod.rs | 10 ++--- pairing/src/bls12_381/ec.rs | 10 ++--- pairing/src/bls12_381/tests/mod.rs | 48 +++++++++++------------ 6 files changed, 54 insertions(+), 56 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 0d85c0ffb5..2602bc7166 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -38,19 +38,19 @@ impl PartialEq for Proof { impl Proof { pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.a.to_compressed().as_ref())?; - writer.write_all(self.b.to_compressed().as_ref())?; - writer.write_all(self.c.to_compressed().as_ref())?; + writer.write_all(self.a.to_bytes().as_ref())?; + writer.write_all(self.b.to_bytes().as_ref())?; + writer.write_all(self.c.to_bytes().as_ref())?; Ok(()) } pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Compressed::default(); + let mut g1_repr = ::Repr::default(); reader.read_exact(g1_repr.as_mut())?; - let affine = E::G1Affine::from_compressed(&g1_repr); + let affine = E::G1Affine::from_bytes(&g1_repr); let affine = if affine.is_some().into() { Ok(affine.unwrap()) } else { @@ -70,10 +70,10 @@ impl Proof { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Compressed::default(); + let mut g2_repr = ::Repr::default(); reader.read_exact(g2_repr.as_mut())?; - let affine = E::G2Affine::from_compressed(&g2_repr); + let affine = E::G2Affine::from_bytes(&g2_repr); let affine = if affine.is_some().into() { Ok(affine.unwrap()) } else { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5fb661bf4b..3ddb98c901 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -446,17 +446,17 @@ impl CurveAffine for Fr { } impl GroupEncoding for Fr { - type Compressed = FakePoint; + type Repr = FakePoint; - fn from_compressed(_bytes: &Self::Compressed) -> CtOption { + fn from_bytes(_bytes: &Self::Repr) -> CtOption { unimplemented!() } - fn from_compressed_unchecked(_bytes: &Self::Compressed) -> CtOption { + fn from_bytes_unchecked(_bytes: &Self::Repr) -> CtOption { unimplemented!() } - fn to_compressed(&self) -> Self::Compressed { + fn to_bytes(&self) -> Self::Repr { unimplemented!() } } diff --git a/group/src/lib.rs b/group/src/lib.rs index 59e25aa76b..0b105c051d 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -156,22 +156,22 @@ pub trait CurveAffine: pub trait GroupEncoding: Sized { /// The encoding of group elements. - type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; + type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; - /// Attempts to deserialize an element from its compressed encoding. - fn from_compressed(bytes: &Self::Compressed) -> CtOption; + /// Attempts to deserialize a group element from its encoding. + fn from_bytes(bytes: &Self::Repr) -> CtOption; - /// Attempts to deserialize a compressed element, not checking if the element is in - /// the correct subgroup. + /// Attempts to deserialize a group element, not checking if the element is valid. /// /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, /// API invariants may be broken.** Please consider using - /// [`CurveAffine::from_compressed`] instead. - fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption; + /// [`GroupEncoding::from_bytes`] instead. + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption; - /// Converts this element into its compressed encoding, so long as it's not - /// the point at infinity. - fn to_compressed(&self) -> Self::Compressed; + /// Converts this element into its byte encoding. This may or may not support + /// encoding the identity. + // TODO: Figure out how to handle identity encoding generically. + fn to_bytes(&self) -> Self::Repr; } /// Affine representation of a point on an elliptic curve that has a defined uncompressed diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index cb1f12f8ae..763cee28eb 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -401,21 +401,21 @@ fn random_compressed_encoding_tests() { ]); assert_eq!( - G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(), + G::Affine::from_bytes(&G::Affine::identity().to_bytes()).unwrap(), G::Affine::identity() ); for _ in 0..1000 { let mut r = G::random(&mut rng).to_affine(); - let compressed = r.to_compressed(); - let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); + let compressed = r.to_bytes(); + let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); assert_eq!(de_compressed, r); r = r.neg(); - let compressed = r.to_compressed(); - let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); + let compressed = r.to_bytes(); + let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); assert_eq!(de_compressed, r); } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 29c591cf26..8d2a3f18cd 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -223,10 +223,10 @@ macro_rules! curve_impl { } impl GroupEncoding for $affine { - type Compressed = $compressed; + type Repr = $compressed; - fn from_compressed(bytes: &Self::Compressed) -> CtOption { - Self::from_compressed_unchecked(bytes).and_then(|affine| { + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes_unchecked(bytes).and_then(|affine| { // NB: Decompression guarantees that it is on the curve already. CtOption::new( affine, @@ -239,7 +239,7 @@ macro_rules! curve_impl { }) } - fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption { + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { if let Ok(p) = bytes.into_affine_unchecked() { CtOption::new(p, Choice::from(1)) } else { @@ -247,7 +247,7 @@ macro_rules! curve_impl { } } - fn to_compressed(&self) -> Self::Compressed { + fn to_bytes(&self) -> Self::Repr { $compressed::from_affine(*self) } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index cac563caff..e905a486a7 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -87,22 +87,20 @@ where fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); - let encoded_len = ::Compressed::default() - .as_ref() - .len(); + let encoded_len = ::Repr::default().as_ref().len(); let mut v = vec![]; { let mut expected = expected; for _ in 0..1000 { let e_affine = e.to_affine(); - let encoded = e_affine.to_compressed(); + let encoded = e_affine.to_bytes(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Compressed::default(); + let mut decoded = ::Repr::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; - let decoded = G::Affine::from_compressed(&decoded).unwrap(); + let decoded = G::Affine::from_bytes(&decoded).unwrap(); assert_eq!(e_affine, decoded); e.add_assign(&G::generator()); @@ -395,12 +393,12 @@ fn test_g2_uncompressed_invalid_vectors() { #[test] fn test_g1_compressed_invalid_vectors() { { - let z = G1Affine::identity().to_compressed(); + let z = G1Affine::identity().to_bytes(); { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -410,7 +408,7 @@ fn test_g1_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -420,7 +418,7 @@ fn test_g1_compressed_invalid_vectors() { for i in 0..G1Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -428,12 +426,12 @@ fn test_g1_compressed_invalid_vectors() { } } - let o = G1Affine::generator().to_compressed(); + let o = G1Affine::generator().to_bytes(); { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -447,7 +445,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { // x coordinate } else { panic!("should have rejected the point") @@ -469,7 +467,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -492,7 +490,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { break; } else { panic!( @@ -509,12 +507,12 @@ fn test_g1_compressed_invalid_vectors() { #[test] fn test_g2_compressed_invalid_vectors() { { - let z = G2Affine::identity().to_compressed(); + let z = G2Affine::identity().to_bytes(); { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -524,7 +522,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -534,7 +532,7 @@ fn test_g2_compressed_invalid_vectors() { for i in 0..G2Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -542,12 +540,12 @@ fn test_g2_compressed_invalid_vectors() { } } - let o = G2Affine::generator().to_compressed(); + let o = G2Affine::generator().to_bytes(); { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -561,7 +559,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // x coordinate (c1) } else { panic!("should have rejected the point") @@ -573,7 +571,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..96].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // x coordinate (c0) } else { panic!("should have rejected the point") @@ -602,7 +600,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -632,7 +630,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { break; } else { panic!( From d52053d877fd051ca62eb11797a340fe67195ebe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 21:52:39 +1200 Subject: [PATCH 062/210] group: Renaming prior to trait refactor This will reduce the size of the subsequent refactor diff. --- bellman/src/domain.rs | 12 ++--- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 8 ++-- bellman/src/groth16/verifier.rs | 4 +- bellman/src/multiexp.rs | 22 ++++----- group/src/lib.rs | 18 ++++---- group/src/tests/mod.rs | 36 +++++++-------- group/src/wnaf.rs | 16 +++---- pairing/src/bls12_381/ec.rs | 56 +++++++++++------------ pairing/src/bls12_381/tests/mod.rs | 6 +-- pairing/src/lib.rs | 10 ++-- pairing/src/tests/engine.rs | 2 +- 13 files changed, 94 insertions(+), 100 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 47f8ff083b..e1dc229701 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -12,7 +12,7 @@ //! [Groth16]: https://eprint.iacr.org/2016/260 use ff::PrimeField; -use group::CurveProjective; +use group::CofactorCurve; use super::SynthesisError; @@ -196,23 +196,23 @@ pub trait Group: Sized + Copy + Clone + Send + Sync { fn group_sub_assign(&mut self, other: &Self); } -pub struct Point(pub G); +pub struct Point(pub G); -impl PartialEq for Point { +impl PartialEq for Point { fn eq(&self, other: &Point) -> bool { self.0 == other.0 } } -impl Copy for Point {} +impl Copy for Point {} -impl Clone for Point { +impl Clone for Point { fn clone(&self) -> Point { *self } } -impl Group for Point { +impl Group for Point { fn group_zero() -> Self { Point(G::identity()) } diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 060ff1becc..e84fc12a9f 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, Wnaf}; +use group::{CurveAffine, CofactorCurve, Group, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index ff91c26758..293cad2b7e 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use futures::Future; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CofactorCurve}; use pairing::Engine; use super::{ParameterSource, Proof}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 3ddb98c901..01e8b82d11 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; +use group::{CurveAffine, CofactorCurve, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -393,7 +393,7 @@ impl Group for Fr { impl PrimeGroup for Fr {} -impl CurveProjective for Fr { +impl CofactorCurve for Fr { type Affine = Fr; fn to_affine(&self) -> Fr { @@ -425,7 +425,7 @@ impl AsRef<[u8]> for FakePoint { } impl CurveAffine for Fr { - type Projective = Fr; + type Curve = Fr; type Scalar = Fr; fn identity() -> Self { @@ -440,7 +440,7 @@ impl CurveAffine for Fr { Choice::from(if ::is_zero(self) { 1 } else { 0 }) } - fn to_projective(&self) -> Self::Projective { + fn to_curve(&self) -> Self::Curve { *self } } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index e86b15aa52..5758d5d83a 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,4 @@ -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CofactorCurve}; use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; @@ -27,7 +27,7 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( return Err(SynthesisError::MalformedVerifyingKey); } - let mut acc = pvk.ic[0].to_projective(); + let mut acc = pvk.ic[0].to_curve(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i)); diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index c9e00d6ba5..fea10c667e 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -2,7 +2,7 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Endianness, Field, PrimeField}; use futures::Future; -use group::{CurveAffine, CurveProjective}; +use group::{CofactorCurve, CurveAffine}; use std::io; use std::iter; use std::ops::AddAssign; @@ -25,17 +25,17 @@ pub trait Source { fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } -pub trait AddAssignFromSource: CurveProjective { +pub trait AddAssignFromSource: CofactorCurve { /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_from_source::Affine>>( + fn add_assign_from_source::Affine>>( &mut self, source: &mut S, ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); + AddAssign::<&::Affine>::add_assign(self, source.next()?); Ok(()) } } -impl AddAssignFromSource for G where G: CurveProjective {} +impl AddAssignFromSource for G where G: CofactorCurve {} impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); @@ -162,8 +162,8 @@ fn multiexp_inner( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, + G: CofactorCurve, + S: SourceBuilder<::Affine>, { // Perform this region of the multiexp let this = { @@ -274,8 +274,8 @@ pub fn multiexp( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, + G: CofactorCurve, + S: SourceBuilder<::Affine>, { let c = if exponents.len() < 32 { 3u32 @@ -296,8 +296,8 @@ where #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { - fn naive_multiexp( - bases: Arc::Affine>>, + fn naive_multiexp( + bases: Arc::Affine>>, exponents: Arc>, ) -> G { assert_eq!(bases.len(), exponents.len()); diff --git a/group/src/lib.rs b/group/src/lib.rs index 0b105c051d..839e31a3b6 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -88,12 +88,10 @@ pub trait PrimeGroup: Group {} /// Projective representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait CurveProjective: - Group - + GroupOps<::Affine> - + GroupOpsOwned<::Affine> +pub trait CofactorCurve: + Group + GroupOps<::Affine> + GroupOpsOwned<::Affine> { - type Affine: CurveAffine + type Affine: CurveAffine + Mul + for<'r> Mul; @@ -134,11 +132,11 @@ pub trait CurveAffine: + 'static + GroupEncoding + Neg - + Mul<::Scalar, Output = ::Projective> - + for<'r> Mul<::Scalar, Output = ::Projective> + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul<::Scalar, Output = ::Curve> { type Scalar: PrimeField; - type Projective: CurveProjective; + type Curve: CofactorCurve; /// Returns the additive identity. fn identity() -> Self; @@ -150,8 +148,8 @@ pub trait CurveAffine: /// additive identity. fn is_identity(&self) -> Choice; - /// Converts this element into its affine representation. - fn to_projective(&self) -> Self::Projective; + /// Converts this element into its efficient representation. + fn to_curve(&self) -> Self::Curve; } pub trait GroupEncoding: Sized { diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 763cee28eb..920b44c911 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,9 +3,9 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; +use crate::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; -pub fn curve_tests() { +pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -50,8 +50,8 @@ pub fn curve_tests() { // Transformations { let a = G::random(&mut rng); - let b = a.to_affine().to_projective(); - let c = a.to_affine().to_projective().to_affine().to_projective(); + let b = a.to_affine().to_curve(); + let c = a.to_affine().to_curve().to_affine().to_curve(); assert_eq!(a, b); assert_eq!(b, c); } @@ -65,7 +65,7 @@ pub fn curve_tests() { random_compressed_encoding_tests::(); } -fn random_wnaf_tests() { +fn random_wnaf_tests() { use crate::wnaf::*; let mut rng = XorShiftRng::from_seed([ @@ -184,7 +184,7 @@ fn random_wnaf_tests() { } } -fn random_negation_tests() { +fn random_negation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -214,7 +214,7 @@ fn random_negation_tests() { } } -fn random_doubling_tests() { +fn random_doubling_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -242,7 +242,7 @@ fn random_doubling_tests() { } } -fn random_multiplication_tests() { +fn random_multiplication_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -277,7 +277,7 @@ fn random_multiplication_tests() { } } -fn random_addition_tests() { +fn random_addition_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -325,17 +325,17 @@ fn random_addition_tests() { // Mixed addition // (a + b) + c - tmp[3] = a_affine.to_projective(); + tmp[3] = a_affine.to_curve(); tmp[3].add_assign(&b_affine); tmp[3].add_assign(&c_affine); // a + (b + c) - tmp[4] = b_affine.to_projective(); + tmp[4] = b_affine.to_curve(); tmp[4].add_assign(&c_affine); tmp[4].add_assign(&a_affine); // (a + c) + b - tmp[5] = a_affine.to_projective(); + tmp[5] = a_affine.to_curve(); tmp[5].add_assign(&c_affine); tmp[5].add_assign(&b_affine); @@ -357,7 +357,7 @@ fn random_addition_tests() { } } -fn random_transformation_tests() { +fn random_transformation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -366,7 +366,7 @@ fn random_transformation_tests() { for _ in 0..1000 { let g = G::random(&mut rng); let g_affine = g.to_affine(); - let g_projective = g_affine.to_projective(); + let g_projective = g_affine.to_curve(); assert_eq!(g, g_projective); } @@ -382,7 +382,7 @@ fn random_transformation_tests() { } for _ in 0..5 { let s = between.sample(&mut rng); - v[s] = v[s].to_affine().to_projective(); + v[s] = v[s].to_affine().to_curve(); } let expected_v = v.iter().map(|v| v.to_affine()).collect::>(); @@ -394,7 +394,7 @@ fn random_transformation_tests() { } } -fn random_compressed_encoding_tests() { +fn random_compressed_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -420,9 +420,9 @@ fn random_compressed_encoding_tests() { } } -pub fn random_uncompressed_encoding_tests() +pub fn random_uncompressed_encoding_tests() where - G::Affine: UncompressedEncoding, + ::Affine: UncompressedEncoding, { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 0ee46cafcb..64a2de1b52 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,10 +2,10 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::{CurveProjective, Group}; +use super::{CofactorCurve, Group}; /// Replaces the contents of `table` with a w-NAF window table for the given window size. -pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { table.truncate(0); table.reserve(1 << (window - 1)); @@ -78,7 +78,7 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize /// /// This function must be provided a `table` and `wnaf` that were constructed with /// the same window size; otherwise, it may panic or produce invalid results. -pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { let mut result = G::identity(); let mut found_one = false; @@ -110,7 +110,7 @@ pub struct Wnaf { window_size: W, } -impl Wnaf<(), Vec, Vec> { +impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { Wnaf { @@ -157,7 +157,7 @@ impl Wnaf<(), Vec, Vec> { } } -impl<'a, G: CurveProjective> Wnaf> { +impl<'a, G: CofactorCurve> Wnaf> { /// Constructs new space for the scalar representation while borrowing /// the computed window table, for sending the window table across threads. pub fn shared(&self) -> Wnaf> { @@ -169,7 +169,7 @@ impl<'a, G: CurveProjective> Wnaf> { } } -impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { +impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { /// Constructs new space for the window table while borrowing /// the computed scalar representation, for sending the scalar representation /// across threads. @@ -184,7 +184,7 @@ impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { impl> Wnaf { /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G + pub fn base(&mut self, base: G) -> G where B: AsMut>, { @@ -195,7 +195,7 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 8d2a3f18cd..e480700f84 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -199,7 +199,7 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; - type Projective = $projective; + type Curve = $projective; fn identity() -> Self { $affine { @@ -217,7 +217,7 @@ macro_rules! curve_impl { Choice::from(if self.infinity { 1 } else { 0 }) } - fn to_projective(&self) -> $projective { + fn to_curve(&self) -> $projective { (*self).into() } } @@ -466,30 +466,28 @@ macro_rules! curve_impl { } } - impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective { + impl<'r> ::std::ops::Add<&'r $affine> for $projective { type Output = Self; #[inline] - fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self { + fn add(self, other: &$affine) -> Self { let mut ret = self; ret.add_assign(other); ret } } - impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::Add<$affine> for $projective { type Output = Self; #[inline] - fn add(self, other: <$projective as CurveProjective>::Affine) -> Self { + fn add(self, other: $affine) -> Self { self + &other } } - impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + impl<'r> ::std::ops::AddAssign<&'r $affine> for $projective { + fn add_assign(&mut self, other: &$affine) { if other.is_identity().into() { return; } @@ -567,44 +565,42 @@ macro_rules! curve_impl { } } - impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::AddAssign<$affine> for $projective { #[inline] - fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + fn add_assign(&mut self, other: $affine) { self.add_assign(&other); } } - impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective { + impl<'r> ::std::ops::Sub<&'r $affine> for $projective { type Output = Self; #[inline] - fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self { + fn sub(self, other: &$affine) -> Self { let mut ret = self; ret.sub_assign(other); ret } } - impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::Sub<$affine> for $projective { type Output = Self; #[inline] - fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self { + fn sub(self, other: $affine) -> Self { self - &other } } - impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + impl<'r> ::std::ops::SubAssign<&'r $affine> for $projective { + fn sub_assign(&mut self, other: &$affine) { self.add_assign(&other.neg()); } } - impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::SubAssign<$affine> for $projective { #[inline] - fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + fn sub_assign(&mut self, other: $affine) { self.sub_assign(&other); } } @@ -746,7 +742,7 @@ macro_rules! curve_impl { impl PrimeGroup for $projective {} - impl CurveProjective for $projective { + impl CofactorCurve for $projective { type Affine = $affine; fn batch_normalize(p: &[Self], q: &mut [$affine]) { @@ -908,7 +904,7 @@ pub mod g1 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -1462,15 +1458,15 @@ pub mod g1 { assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); - let mut tmp1 = a.to_projective(); - tmp1.add_assign(&b.to_projective()); + let mut tmp1 = a.to_curve(); + tmp1.add_assign(&b.to_curve()); assert_eq!(tmp1.to_affine(), c); - assert_eq!(tmp1, c.to_projective()); + assert_eq!(tmp1, c.to_curve()); - let mut tmp2 = a.to_projective(); + let mut tmp2 = a.to_curve(); tmp2.add_assign(&b); assert_eq!(tmp2.to_affine(), c); - assert_eq!(tmp2, c.to_projective()); + assert_eq!(tmp2, c.to_curve()); } #[test] @@ -1487,7 +1483,7 @@ pub mod g2 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index e905a486a7..1d9f41fc9d 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; +use group::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; use super::*; use crate::*; @@ -55,7 +55,7 @@ fn test_pairing_result_against_relic() { }); } -fn uncompressed_test_vectors(expected: &[u8]) +fn uncompressed_test_vectors(expected: &[u8]) where G::Affine: UncompressedEncoding, { @@ -85,7 +85,7 @@ where assert_eq!(&v[..], expected); } -fn compressed_test_vectors(expected: &[u8]) { +fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); let encoded_len = ::Repr::default().as_ref().len(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 6c3e2b47f8..6898bd5532 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,7 +23,7 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, + CofactorCurve, CurveAffine, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; @@ -35,7 +35,7 @@ pub trait Engine: Sized + 'static + Clone { type Fr: PrimeField; /// The projective representation of an element in G1. - type G1: CurveProjective + type G1: CofactorCurve + From + GroupOps + GroupOpsOwned @@ -45,7 +45,7 @@ pub trait Engine: Sized + 'static + Clone { /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< Scalar = Self::Fr, - Projective = Self::G1, + Curve = Self::G1, Pair = Self::G2Affine, PairingResult = Self::Gt, > + From @@ -53,7 +53,7 @@ pub trait Engine: Sized + 'static + Clone { + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; /// The projective representation of an element in G2. - type G2: CurveProjective + type G2: CofactorCurve + From + GroupOps + GroupOpsOwned @@ -63,7 +63,7 @@ pub trait Engine: Sized + 'static + Clone { /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< Scalar = Self::Fr, - Projective = Self::G2, + Curve = Self::G2, Pair = Self::G1Affine, PairingResult = Self::Gt, > + From diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index bf0f8849d7..2cfc473b25 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::{Endianness, Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group}; +use group::{CofactorCurve, CurveAffine, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; From a105ad675aa2ff9e64b61e35c76b161d655398fe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 00:49:44 +1200 Subject: [PATCH 063/210] group: Separate prime and cofactor traits into modules Instead of having the Group crate hold a Subgroup associated type (and thus needing to define the subgroup of a prime-order group as itself), we specify two separate sets of traits for prime-order groups and ones with a cofactor. Protocol implementors can either restrict their implementations to only work with PrimeGroup, or can explicitly choose to support CofactorGroup and then explicitly handle the subgroup edge cases with e.g. CofactorGroup::mul_by_cofactor (which would be a no-op for PrimeGroup). Protocol implementors can also choose to specialise to elliptic curves if they want to leverage an affine representation and mixed addition in their protocol for efficiency, or they can ignore those traits and stick with the simpler group-focused traits. --- bellman/src/domain.rs | 2 +- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/mod.rs | 2 +- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 29 +- bellman/src/groth16/verifier.rs | 2 +- bellman/src/multiexp.rs | 12 +- ff/src/lib.rs | 2 + group/src/cofactor.rs | 113 ++++++++ group/src/lib.rs | 68 +---- group/src/prime.rs | 52 ++++ group/src/tests/mod.rs | 5 +- group/src/wnaf.rs | 6 +- pairing/src/bls12_381/ec.rs | 324 +++++++++++++++++++++- pairing/src/bls12_381/mod.rs | 2 +- pairing/src/bls12_381/tests/mod.rs | 5 +- pairing/src/lib.rs | 6 +- pairing/src/tests/engine.rs | 2 +- 18 files changed, 547 insertions(+), 89 deletions(-) create mode 100644 group/src/cofactor.rs create mode 100644 group/src/prime.rs diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index e1dc229701..098b0edda9 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -12,7 +12,7 @@ //! [Groth16]: https://eprint.iacr.org/2016/260 use ff::PrimeField; -use group::CofactorCurve; +use group::cofactor::CofactorCurve; use super::SynthesisError; diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index e84fc12a9f..084e7a3768 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve, Group, Wnaf}; +use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 2602bc7166..7c97197aaa 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{CurveAffine, GroupEncoding, UncompressedEncoding}; +use group::{cofactor::CofactorCurveAffine, GroupEncoding, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 293cad2b7e..cbe883aa7a 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use futures::Future; use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve}; +use group::{cofactor::CofactorCurveAffine, Curve}; use pairing::Engine; use super::{ParameterSource, Proof}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 01e8b82d11..17e0043a1c 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,9 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; +use group::{ + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, +}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -367,7 +371,6 @@ impl MillerLoopResult for Fr { } impl Group for Fr { - type Subgroup = Fr; type Scalar = Fr; fn random(rng: &mut R) -> Self { @@ -393,8 +396,20 @@ impl Group for Fr { impl PrimeGroup for Fr {} -impl CofactorCurve for Fr { - type Affine = Fr; +impl CofactorGroup for Fr { + type Subgroup = Fr; + + fn mul_by_cofactor(&self) -> Self::Subgroup { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, Choice::from(1)) + } +} + +impl Curve for Fr { + type AffineRepr = Fr; fn to_affine(&self) -> Fr { *self @@ -409,6 +424,10 @@ impl CofactorCurve for Fr { } } +impl CofactorCurve for Fr { + type Affine = Fr; +} + #[derive(Copy, Clone, Default)] pub struct FakePoint; @@ -424,7 +443,7 @@ impl AsRef<[u8]> for FakePoint { } } -impl CurveAffine for Fr { +impl CofactorCurveAffine for Fr { type Curve = Fr; type Scalar = Fr; diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 5758d5d83a..0fe8c94609 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,4 @@ -use group::{CurveAffine, CofactorCurve}; +use group::{cofactor::CofactorCurveAffine, Curve}; use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index fea10c667e..8fdbc70039 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -2,7 +2,7 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Endianness, Field, PrimeField}; use futures::Future; -use group::{CofactorCurve, CurveAffine}; +use group::cofactor::{CofactorCurve, CofactorCurveAffine}; use std::io; use std::iter; use std::ops::AddAssign; @@ -11,14 +11,14 @@ use std::sync::Arc; use super::SynthesisError; /// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { +pub trait SourceBuilder: Send + Sync + 'static + Clone { type Source: Source; fn new(self) -> Self::Source; } /// A source of bases, like an iterator. -pub trait Source { +pub trait Source { fn next(&mut self) -> Result<&G, SynthesisError>; /// Skips `amt` elements from the source, avoiding deserialization. @@ -37,7 +37,7 @@ pub trait AddAssignFromSource: CofactorCurve { } impl AddAssignFromSource for G where G: CofactorCurve {} -impl SourceBuilder for (Arc>, usize) { +impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); fn new(self) -> (Arc>, usize) { @@ -45,7 +45,7 @@ impl SourceBuilder for (Arc>, usize) { } } -impl Source for (Arc>, usize) { +impl Source for (Arc>, usize) { fn next(&mut self) -> Result<&G, SynthesisError> { if self.0.len() <= self.1 { return Err(io::Error::new( @@ -311,7 +311,7 @@ fn test_with_bls12() { acc } - use group::Group; + use group::{Curve, Group}; use pairing::{ bls12_381::{Bls12, Fr}, Engine, diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e975d91361..9d96f5edeb 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -213,6 +213,8 @@ pub trait PrimeField: Field + From { fn root_of_unity() -> Self; } +/// Takes a little-endian representation of some value, and returns its bits in big-endian +/// order. #[derive(Debug)] pub struct BitIterator> { t: E, diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs new file mode 100644 index 0000000000..9ccc983940 --- /dev/null +++ b/group/src/cofactor.rs @@ -0,0 +1,113 @@ +use core::fmt; +use core::ops::{Mul, Neg}; +use ff::{BitIterator, Endianness, PrimeField}; +use subtle::{Choice, CtOption}; + +use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned}; + +/// This trait represents an element of a cryptographic group with a large prime-order +/// subgroup and a comparatively-small cofactor. +pub trait CofactorGroup: + Group + + GroupEncoding + + GroupOps<::Subgroup> + + GroupOpsOwned<::Subgroup> +{ + /// The large prime-order subgroup in which cryptographic operations are performed. + /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. + type Subgroup: PrimeGroup + Into; + + /// Returns `[h] self`, where `h` is the cofactor of the group. + /// + /// If `Self` implements [`PrimeGroup`], this returns `self`. + fn mul_by_cofactor(&self) -> Self::Subgroup; + + /// Returns `self` if it is contained in the prime-order subgroup. + /// + /// If `Self` implements [`PrimeGroup`], this returns `Some(self)`. + fn into_subgroup(self) -> CtOption; + + /// Determines if this element is of small order. + /// + /// Returns: + /// - `true` if `self` is in the torsion subgroup. + /// - `false` if `self` is not in the torsion subgroup. + fn is_small_order(&self) -> Choice { + self.mul_by_cofactor().is_identity() + } + + /// Determines if this element is "torsion free", i.e., is contained in the + /// prime-order subgroup. + /// + /// Returns: + /// - `true` if `self` has zero torsion component and is in the prime-order subgroup. + /// - `false` if `self` has non-zero torsion component and is not in the prime-order + /// subgroup. + fn is_torsion_free(&self) -> Choice { + // Obtain the scalar field characteristic in little endian. + let mut char = Self::Scalar::char(); + ::ReprEndianness::toggle_little_endian(&mut char); + + // Multiply self by the characteristic to eliminate any prime-order subgroup + // component. + let bits = BitIterator::::new(char); + let mut res = Self::identity(); + for i in bits { + res = res.double(); + if i { + res.add_assign(self) + } + } + + // If the result is the identity, there was zero torsion component! + res.is_identity() + } +} + +/// Efficient representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CofactorCurve: + Curve::Affine> + CofactorGroup +{ + type Affine: CofactorCurveAffine + + Mul + + for<'r> Mul; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CofactorCurveAffine: + GroupEncoding + + Copy + + Clone + + Sized + + Send + + Sync + + fmt::Debug + + fmt::Display + + PartialEq + + Eq + + 'static + + Neg + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul< + ::Scalar, + Output = ::Curve, + > +{ + type Scalar: PrimeField; + type Curve: CofactorCurve; + + /// Returns the additive identity. + fn identity() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn generator() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_identity(&self) -> Choice; + + /// Converts this element to its curve representation. + fn to_curve(&self) -> Self::Curve; +} diff --git a/group/src/lib.rs b/group/src/lib.rs index 839e31a3b6..9696df42d7 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -8,6 +8,8 @@ use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, CtOption}; +pub mod cofactor; +pub mod prime; pub mod tests; mod wnaf; @@ -54,16 +56,10 @@ pub trait Group: + Neg + GroupOps + GroupOpsOwned - + GroupOps<::Subgroup> - + GroupOpsOwned<::Subgroup> + ScalarMul<::Scalar> + ScalarMulOwned<::Scalar> { - /// The large prime-order subgroup in which cryptographic operations are performed. - /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. - type Subgroup: PrimeGroup; - - /// Scalars modulo the order of [`Group::Subgroup`]. + /// Scalars modulo the order of this group's scalar field. type Scalar: PrimeField; /// Returns an element chosen uniformly at random using a user-provided RNG. @@ -73,7 +69,7 @@ pub trait Group: fn identity() -> Self; /// Returns a fixed generator of the prime-order subgroup. - fn generator() -> Self::Subgroup; + fn generator() -> Self; /// Determines if this point is the identity. fn is_identity(&self) -> Choice; @@ -83,21 +79,16 @@ pub trait Group: fn double(&self) -> Self; } -/// This trait represents an element of a prime-order cryptographic group. -pub trait PrimeGroup: Group {} - -/// Projective representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CofactorCurve: - Group + GroupOps<::Affine> + GroupOpsOwned<::Affine> +/// Efficient representation of an elliptic curve point guaranteed. +pub trait Curve: + Group + GroupOps<::AffineRepr> + GroupOpsOwned<::AffineRepr> { - type Affine: CurveAffine - + Mul - + for<'r> Mul; + /// The affine representation for this elliptic curve. + type AffineRepr; /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { assert_eq!(p.len(), q.len()); for (p, q) in p.iter().zip(q.iter_mut()) { @@ -106,7 +97,7 @@ pub trait CofactorCurve: } /// Converts this element into its affine representation. - fn to_affine(&self) -> Self::Affine; + fn to_affine(&self) -> Self::AffineRepr; /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. @@ -117,41 +108,6 @@ pub trait CofactorCurve: fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; } -/// Affine representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CurveAffine: - Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + fmt::Display - + PartialEq - + Eq - + 'static - + GroupEncoding - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul<::Scalar, Output = ::Curve> -{ - type Scalar: PrimeField; - type Curve: CofactorCurve; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; - - /// Converts this element into its efficient representation. - fn to_curve(&self) -> Self::Curve; -} - pub trait GroupEncoding: Sized { /// The encoding of group elements. type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; @@ -174,7 +130,7 @@ pub trait GroupEncoding: Sized { /// Affine representation of a point on an elliptic curve that has a defined uncompressed /// encoding. -pub trait UncompressedEncoding: CurveAffine { +pub trait UncompressedEncoding: Sized { type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its uncompressed encoding. diff --git a/group/src/prime.rs b/group/src/prime.rs new file mode 100644 index 0000000000..d02105d794 --- /dev/null +++ b/group/src/prime.rs @@ -0,0 +1,52 @@ +use core::fmt; +use core::ops::{Mul, Neg}; +use ff::PrimeField; +use subtle::Choice; + +use crate::{Curve, Group, GroupEncoding}; + +/// This trait represents an element of a prime-order cryptographic group. +pub trait PrimeGroup: Group + GroupEncoding {} + +/// Efficient representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait PrimeCurve: Curve::Affine> + PrimeGroup { + type Affine: PrimeCurveAffine + + Mul + + for<'r> Mul; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait PrimeCurveAffine: + GroupEncoding + + Copy + + Clone + + Sized + + Send + + Sync + + fmt::Debug + + fmt::Display + + PartialEq + + Eq + + 'static + + Neg + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul<::Scalar, Output = ::Curve> +{ + type Scalar: PrimeField; + type Curve: PrimeCurve; + + /// Returns the additive identity. + fn identity() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn generator() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_identity(&self) -> Choice; + + /// Converts this element to its curve representation. + fn to_curve(&self) -> Self::Curve; +} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 920b44c911..50ba581354 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,10 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; +use crate::{ + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupEncoding, UncompressedEncoding, +}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 64a2de1b52..98573b4ade 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,7 +2,7 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::{CofactorCurve, Group}; +use super::{cofactor::CofactorCurve, Group}; /// Replaces the contents of `table` with a w-NAF window table for the given window size. pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { @@ -92,9 +92,9 @@ pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { found_one = true; if *n > 0 { - result.add_assign(&table[(n / 2) as usize]); + result += &table[(n / 2) as usize]; } else { - result.sub_assign(&table[((-n) / 2) as usize]); + result -= &table[((-n) / 2) as usize]; } } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index e480700f84..bf4d962405 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -2,6 +2,7 @@ macro_rules! curve_impl { ( $name:expr, $projective:ident, + $subgroup:ident, $affine:ident, $basefield:ident, $scalarfield:ident, @@ -100,6 +101,21 @@ macro_rules! curve_impl { } } + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct $subgroup($projective); + + impl ::std::fmt::Display for $subgroup { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl From<$subgroup> for $projective { + fn from(val: $subgroup) -> $projective { + val.0 + } + } + impl $affine { fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { let mut res = $projective::identity(); @@ -197,7 +213,7 @@ macro_rules! curve_impl { } } - impl CurveAffine for $affine { + impl CofactorCurveAffine for $affine { type Scalar = $scalarfield; type Curve = $projective; @@ -222,6 +238,70 @@ macro_rules! curve_impl { } } + impl GroupEncoding for $projective { + type Repr = $compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + if let Ok(affine) = bytes.into_affine_unchecked() { + // NB: Decompression guarantees that it is on the curve already. + CtOption::new( + affine.into(), + Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }), + ) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new(p.into(), Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn to_bytes(&self) -> Self::Repr { + self.to_affine().to_bytes() + } + } + + impl GroupEncoding for $subgroup { + type Repr = $compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + if let Ok(affine) = bytes.into_affine_unchecked() { + // NB: Decompression guarantees that it is on the curve already. + CtOption::new( + $subgroup(affine.into()), + Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }), + ) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new($subgroup(p.into()), Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn to_bytes(&self) -> Self::Repr { + self.0.to_bytes() + } + } + impl GroupEncoding for $affine { type Repr = $compressed; @@ -651,8 +731,182 @@ macro_rules! curve_impl { } } + impl ::std::iter::Sum for $subgroup { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + + impl<'r> ::std::iter::Sum<&'r $subgroup> for $subgroup { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + + impl ::std::ops::Neg for $subgroup { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + $subgroup(self.0.neg()) + } + } + + impl<'r> ::std::ops::Add<&'r $subgroup> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: &$subgroup) -> Self { + self + &other.0 + } + } + + impl ::std::ops::Add<$subgroup> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: $subgroup) -> Self { + self + &other.0 + } + } + + impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $projective { + fn add_assign(&mut self, other: &$subgroup) { + self.add_assign(&other.0) + } + } + + impl ::std::ops::AddAssign<$subgroup> for $projective { + #[inline] + fn add_assign(&mut self, other: $subgroup) { + self.add_assign(&other.0); + } + } + + impl<'r> ::std::ops::Sub<&'r $subgroup> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: &$subgroup) -> Self { + self - &other.0 + } + } + + impl ::std::ops::Sub<$subgroup> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: $subgroup) -> Self { + self - &other.0 + } + } + + impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $projective { + fn sub_assign(&mut self, other: &$subgroup) { + self.sub_assign(&other.0); + } + } + + impl ::std::ops::SubAssign<$subgroup> for $projective { + #[inline] + fn sub_assign(&mut self, other: $subgroup) { + self.sub_assign(&other.0); + } + } + + impl<'r> ::std::ops::Add<&'r $subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn add(self, other: &$subgroup) -> Self { + $subgroup(self.0 + &other.0) + } + } + + impl ::std::ops::Add<$subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn add(self, other: $subgroup) -> Self { + $subgroup(self.0 + &other.0) + } + } + + impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $subgroup { + fn add_assign(&mut self, other: &$subgroup) { + self.0.add_assign(&other.0) + } + } + + impl ::std::ops::AddAssign<$subgroup> for $subgroup { + #[inline] + fn add_assign(&mut self, other: $subgroup) { + self.0.add_assign(&other.0); + } + } + + impl<'r> ::std::ops::Sub<&'r $subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn sub(self, other: &$subgroup) -> Self { + $subgroup(self.0 - &other.0) + } + } + + impl ::std::ops::Sub<$subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn sub(self, other: $subgroup) -> Self { + $subgroup(self.0 - &other.0) + } + } + + impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $subgroup { + fn sub_assign(&mut self, other: &$subgroup) { + self.0.sub_assign(&other.0); + } + } + + impl ::std::ops::SubAssign<$subgroup> for $subgroup { + #[inline] + fn sub_assign(&mut self, other: $subgroup) { + self.0.sub_assign(&other.0); + } + } + + impl ::std::ops::Mul<<$projective as Group>::Scalar> for $subgroup { + type Output = Self; + + fn mul(mut self, other: <$projective as Group>::Scalar) -> Self { + self.0.mul_assign(&other); + self + } + } + + impl<'r> ::std::ops::Mul<&'r <$projective as Group>::Scalar> for $subgroup { + type Output = Self; + + fn mul(mut self, other: &'r <$projective as Group>::Scalar) -> Self { + self.0.mul_assign(other); + self + } + } + + impl ::std::ops::MulAssign<<$projective as Group>::Scalar> for $subgroup { + fn mul_assign(&mut self, other: <$projective as Group>::Scalar) { + self.0.mul_assign(&other); + } + } + + impl<'r> ::std::ops::MulAssign<&'r <$projective as Group>::Scalar> for $subgroup { + fn mul_assign(&mut self, other: &'r <$projective as Group>::Scalar) { + self.0.mul_assign(other) + } + } + impl Group for $projective { - type Subgroup = Self; type Scalar = $scalarfield; fn random(rng: &mut R) -> Self { @@ -740,10 +994,56 @@ macro_rules! curve_impl { } } - impl PrimeGroup for $projective {} + impl Group for $subgroup { + type Scalar = $scalarfield; - impl CofactorCurve for $projective { - type Affine = $affine; + fn random(rng: &mut R) -> Self { + $subgroup($projective::random(rng)) + } + + fn identity() -> Self { + $subgroup($projective::identity()) + } + + fn generator() -> Self { + $subgroup($projective::generator()) + } + + fn is_identity(&self) -> Choice { + self.0.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + $subgroup(self.0.double()) + } + } + + impl PrimeGroup for $subgroup {} + + impl CofactorGroup for $projective { + type Subgroup = $subgroup; + + fn mul_by_cofactor(&self) -> Self::Subgroup { + $subgroup($affine::from(*self).scale_by_cofactor().into()) + } + + fn into_subgroup(self) -> CtOption { + CtOption::new( + $subgroup(self), + Choice::from( + if $affine::from(self).is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }, + ), + ) + } + } + + impl Curve for $projective { + type AffineRepr = $affine; fn batch_normalize(p: &[Self], q: &mut [$affine]) { assert_eq!(p.len(), q.len()); @@ -804,6 +1104,10 @@ macro_rules! curve_impl { } } + impl CofactorCurve for $projective { + type Affine = $affine; + } + // The affine point X, Y is represented in the jacobian // coordinates with Z = 1. impl From<$affine> for $projective { @@ -904,7 +1208,9 @@ pub mod g1 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -914,6 +1220,7 @@ pub mod g1 { curve_impl!( "G1", G1, + G1Subgroup, G1Affine, Fq, Fr, @@ -1483,7 +1790,9 @@ pub mod g2 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -1493,6 +1802,7 @@ pub mod g2 { curve_impl!( "G2", G2, + G2Subgroup, G2Affine, Fq2, Fr, diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index c0fb39356c..0c3de349e7 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -24,7 +24,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, MillerLoopResult, MultiMillerLoop}; use ff::{BitIterator, Field}; -use group::CurveAffine; +use group::cofactor::CofactorCurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; // The BLS parameter x for BLS12-381 is -0xd201000000010000 diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 1d9f41fc9d..b698c0a0f5 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,8 @@ use ff::PrimeField; -use group::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; +use group::{ + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupEncoding, UncompressedEncoding, +}; use super::*; use crate::*; diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 6898bd5532..e25d364e54 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,8 +23,8 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, - UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -80,7 +80,7 @@ pub trait Engine: Sized + 'static + Clone { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. -pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { +pub trait PairingCurveAffine: CofactorCurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; type PairingResult: Field; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 2cfc473b25..6e971622fe 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::{Endianness, Field, PrimeField}; -use group::{CofactorCurve, CurveAffine, Group}; +use group::{cofactor::CofactorCurveAffine, Curve, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; From ad96a38750b1f23c18765403f3b3faa26bd23d31 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 6 Jun 2020 11:29:26 +1200 Subject: [PATCH 064/210] group: Make Wnaf generic over Group Wnaf was originally generic over CurveProjective; in the prior refactor commit, we renamed this to CofactorCurve. But w-NAF only requires scalar multiplication, which is provided by the Group trait, so we relax the bounds on Wnaf to enable it to be used with any group. We move the generic w-NAF helper methods from the Curve trait to a new WnafGroup extension trait, to keep the w-NAF API surface self-contained, and not expose it to users who aren't using it. --- bellman/src/groth16/generator.rs | 6 ++++- bellman/src/groth16/tests/dummy_engine.rs | 4 +++- group/src/lib.rs | 10 +------- group/src/tests/mod.rs | 4 ++-- group/src/wnaf.rs | 29 ++++++++++++++++------- pairing/src/bls12_381/ec.rs | 12 ++++++---- 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 084e7a3768..d04ed6c0a0 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf}; +use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf, WnafGroup}; use pairing::Engine; use super::{Parameters, VerifyingKey}; @@ -22,6 +22,8 @@ pub fn generate_random_parameters( ) -> Result, SynthesisError> where E: Engine, + E::G1: WnafGroup, + E::G2: WnafGroup, C: Circuit, R: RngCore, { @@ -165,6 +167,8 @@ pub fn generate_parameters( ) -> Result, SynthesisError> where E: Engine, + E::G1: WnafGroup, + E::G2: WnafGroup, C: Circuit, { let mut assembly = KeypairAssembly { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 17e0043a1c..8f11fe1549 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -2,7 +2,7 @@ use ff::{Field, PrimeField}; use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; @@ -414,7 +414,9 @@ impl Curve for Fr { fn to_affine(&self) -> Fr { *self } +} +impl WnafGroup for Fr { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { 3 } diff --git a/group/src/lib.rs b/group/src/lib.rs index 9696df42d7..57914f8207 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -13,7 +13,7 @@ pub mod prime; pub mod tests; mod wnaf; -pub use self::wnaf::Wnaf; +pub use self::wnaf::{Wnaf, WnafGroup}; /// A helper trait for types with a group operation. pub trait GroupOps: @@ -98,14 +98,6 @@ pub trait Curve: /// Converts this element into its affine representation. fn to_affine(&self) -> Self::AffineRepr; - - /// Recommends a wNAF window table size given a scalar. Always returns a number - /// between 2 and 22, inclusive. - fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> usize; - - /// Recommends a wNAF window size given the number of scalars you intend to multiply - /// a base by. Always returns a number between 2 and 22, inclusive. - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; } pub trait GroupEncoding: Sized { diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 50ba581354..be383b1676 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -5,6 +5,7 @@ use std::ops::{Mul, Neg}; use crate::{ cofactor::{CofactorCurve, CofactorCurveAffine}, + wnaf::WnafGroup, GroupEncoding, UncompressedEncoding, }; @@ -64,11 +65,10 @@ pub fn curve_tests() { random_doubling_tests::(); random_negation_tests::(); random_transformation_tests::(); - random_wnaf_tests::(); random_compressed_encoding_tests::(); } -fn random_wnaf_tests() { +pub fn random_wnaf_tests() { use crate::wnaf::*; let mut rng = XorShiftRng::from_seed([ diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 98573b4ade..20651d4379 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,10 +2,21 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::{cofactor::CofactorCurve, Group}; +use super::Group; + +/// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`]. +pub trait WnafGroup: Group { + /// Recommends a wNAF window table size given a scalar. Always returns a number + /// between 2 and 22, inclusive. + fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> usize; + + /// Recommends a wNAF window size given the number of scalars you intend to multiply + /// a base by. Always returns a number between 2 and 22, inclusive. + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; +} /// Replaces the contents of `table` with a w-NAF window table for the given window size. -pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { table.truncate(0); table.reserve(1 << (window - 1)); @@ -78,7 +89,7 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize /// /// This function must be provided a `table` and `wnaf` that were constructed with /// the same window size; otherwise, it may panic or produce invalid results. -pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { let mut result = G::identity(); let mut found_one = false; @@ -110,7 +121,7 @@ pub struct Wnaf { window_size: W, } -impl Wnaf<(), Vec, Vec> { +impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { Wnaf { @@ -119,7 +130,9 @@ impl Wnaf<(), Vec, Vec> { window_size: (), } } +} +impl Wnaf<(), Vec, Vec> { /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that /// can perform exponentiations with `.scalar(..)`. pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf> { @@ -157,7 +170,7 @@ impl Wnaf<(), Vec, Vec> { } } -impl<'a, G: CofactorCurve> Wnaf> { +impl<'a, G: Group> Wnaf> { /// Constructs new space for the scalar representation while borrowing /// the computed window table, for sending the window table across threads. pub fn shared(&self) -> Wnaf> { @@ -169,7 +182,7 @@ impl<'a, G: CofactorCurve> Wnaf> { } } -impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { +impl<'a, G: Group> Wnaf, &'a [i64]> { /// Constructs new space for the window table while borrowing /// the computed scalar representation, for sending the scalar representation /// across threads. @@ -184,7 +197,7 @@ impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { impl> Wnaf { /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G + pub fn base(&mut self, base: G) -> G where B: AsMut>, { @@ -195,7 +208,7 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index bf4d962405..4874558448 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -1092,7 +1092,9 @@ macro_rules! curve_impl { fn to_affine(&self) -> $affine { (*self).into() } + } + impl WnafGroup for $projective { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { Self::empirical_recommended_wnaf_for_scalar( ::NUM_BITS as usize, @@ -1210,7 +1212,7 @@ pub mod g1 { use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; use std::fmt; @@ -1778,8 +1780,9 @@ pub mod g1 { #[test] fn g1_curve_tests() { - use group::tests::{curve_tests, random_uncompressed_encoding_tests}; + use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests}; curve_tests::(); + random_wnaf_tests::(); random_uncompressed_encoding_tests::(); } } @@ -1792,7 +1795,7 @@ pub mod g2 { use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; use std::fmt; @@ -2484,8 +2487,9 @@ pub mod g2 { #[test] fn g2_curve_tests() { - use group::tests::{curve_tests, random_uncompressed_encoding_tests}; + use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests}; curve_tests::(); + random_wnaf_tests::(); random_uncompressed_encoding_tests::(); } } From 12f6ec1b2efa73a23f163789d7838654755440db Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 23 Jun 2020 23:15:00 +1200 Subject: [PATCH 065/210] pairing: Bound Engine::Gt on Group instead of Field --- pairing/src/bls12_381/ec.rs | 10 +- pairing/src/bls12_381/mod.rs | 184 +++++++++++++++++++++++++++-- pairing/src/bls12_381/tests/mod.rs | 4 +- pairing/src/lib.rs | 10 +- pairing/src/tests/engine.rs | 22 ++-- 5 files changed, 197 insertions(+), 33 deletions(-) diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 4874558448..3af8dc5e9c 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -367,7 +367,7 @@ macro_rules! curve_impl { impl PairingCurveAffine for $affine { type Pair = $pairing; - type PairingResult = Fq12; + type PairingResult = Gt; fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { self.perform_pairing(other) @@ -1205,7 +1205,7 @@ impl fmt::Display for GroupDecodingError { } pub mod g1 { - use super::super::{Fq, Fq12, FqRepr, Fr}; + use super::super::{Fq, FqRepr, Fr, Gt}; use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; @@ -1446,7 +1446,7 @@ pub mod g1 { super::super::fq::B_COEFF } - fn perform_pairing(&self, other: &G2Affine) -> Fq12 { + fn perform_pairing(&self, other: &G2Affine) -> Gt { super::super::Bls12::pairing(self, other) } } @@ -1788,7 +1788,7 @@ pub mod g1 { } pub mod g2 { - use super::super::{Fq, Fq12, Fq2, FqRepr, Fr}; + use super::super::{Fq, Fq2, FqRepr, Fr, Gt}; use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; @@ -2078,7 +2078,7 @@ pub mod g2 { self.mul_bits_u64(cofactor) } - fn perform_pairing(&self, other: &G1Affine) -> Fq12 { + fn perform_pairing(&self, other: &G1Affine) -> Gt { super::super::Bls12::pairing(other, self) } } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 0c3de349e7..99ba70a06f 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -23,14 +23,184 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, MillerLoopResult, MultiMillerLoop}; -use ff::{BitIterator, Field}; -use group::cofactor::CofactorCurveAffine; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use ff::{BitIterator, Field, PrimeField}; +use group::{cofactor::CofactorCurveAffine, Group}; +use rand_core::RngCore; +use std::fmt; +use std::iter::Sum; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable}; // The BLS parameter x for BLS12-381 is -0xd201000000010000 const BLS_X: u64 = 0xd201000000010000; const BLS_X_IS_NEGATIVE: bool = true; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Gt(Fq12); + +impl fmt::Display for Gt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl ConditionallySelectable for Gt { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Gt(Fq12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Neg for Gt { + type Output = Gt; + + fn neg(self) -> Self::Output { + let mut ret = self.0; + ret.conjugate(); + Gt(ret) + } +} + +impl Sum for Gt { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), Add::add) + } +} + +impl<'r> Sum<&'r Gt> for Gt { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), Add::add) + } +} + +impl Add for Gt { + type Output = Gt; + + fn add(self, rhs: Self) -> Self::Output { + Gt(self.0 * rhs.0) + } +} + +impl Add<&Gt> for Gt { + type Output = Gt; + + fn add(self, rhs: &Gt) -> Self::Output { + Gt(self.0 * rhs.0) + } +} + +impl AddAssign for Gt { + fn add_assign(&mut self, rhs: Self) { + self.0 *= rhs.0; + } +} + +impl AddAssign<&Gt> for Gt { + fn add_assign(&mut self, rhs: &Gt) { + self.0 *= rhs.0; + } +} + +impl Sub for Gt { + type Output = Gt; + + fn sub(self, rhs: Self) -> Self::Output { + self + (-rhs) + } +} + +impl Sub<&Gt> for Gt { + type Output = Gt; + + fn sub(self, rhs: &Gt) -> Self::Output { + self + (-*rhs) + } +} + +impl SubAssign for Gt { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl SubAssign<&Gt> for Gt { + fn sub_assign(&mut self, rhs: &Gt) { + *self = *self - rhs; + } +} + +impl Mul<&Fr> for Gt { + type Output = Gt; + + fn mul(self, other: &Fr) -> Self::Output { + let mut acc = Self::identity(); + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fr + // elements. + for bit in other + .to_repr() + .as_ref() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = Gt::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl Mul for Gt { + type Output = Gt; + + fn mul(self, other: Fr) -> Self::Output { + self * &other + } +} + +impl<'r> MulAssign<&'r Fr> for Gt { + fn mul_assign(&mut self, other: &'r Fr) { + *self = *self * other + } +} + +impl MulAssign for Gt { + fn mul_assign(&mut self, other: Fr) { + self.mul_assign(&other); + } +} + +impl Group for Gt { + type Scalar = Fr; + + fn random(_rng: &mut R) -> Self { + unimplemented!() + } + + fn identity() -> Self { + Gt(Fq12::one()) + } + + fn generator() -> Self { + unimplemented!() + } + + fn is_identity(&self) -> Choice { + Choice::from(if self.0 == Fq12::one() { 1 } else { 0 }) + } + + #[must_use] + fn double(&self) -> Self { + Gt(self.0.square()) + } +} + #[derive(Clone, Debug)] pub struct Bls12; @@ -40,7 +210,7 @@ impl Engine for Bls12 { type G1Affine = G1Affine; type G2 = G2; type G2Affine = G2Affine; - type Gt = Fq12; + type Gt = Gt; fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation() @@ -109,9 +279,9 @@ impl MultiMillerLoop for Bls12 { } impl MillerLoopResult for Fq12 { - type Gt = Fq12; + type Gt = Gt; - fn final_exponentiation(&self) -> Fq12 { + fn final_exponentiation(&self) -> Gt { let mut f1 = *self; f1.conjugate(); @@ -162,7 +332,7 @@ impl MillerLoopResult for Fq12 { y2.frobenius_map(1); y1.mul_assign(&y2); - y1 + Gt(y1) }) // self must be nonzero. .unwrap() diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index b698c0a0f5..137694c43a 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -26,7 +26,7 @@ fn test_pairing_result_against_relic() { 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 */ - assert_eq!(Bls12::pairing(&G1Affine::generator(), &G2Affine::generator()), Fq12 { + assert_eq!(Bls12::pairing(&G1Affine::generator(), &G2Affine::generator()), Gt(Fq12 { c0: Fq6 { c0: Fq2 { c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(), @@ -55,7 +55,7 @@ fn test_pairing_result_against_relic() { c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap() } } - }); + })); } fn uncompressed_test_vectors(expected: &[u8]) diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index e25d364e54..bb57d21a79 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -21,10 +21,10 @@ pub mod tests; pub mod bls12_381; use core::ops::Mul; -use ff::{Field, PrimeField}; +use ff::PrimeField; use group::{ cofactor::{CofactorCurve, CofactorCurveAffine}, - GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, + Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -71,7 +71,7 @@ pub trait Engine: Sized + 'static + Clone { + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; /// The extension field that hosts the target group of the pairing. - type Gt: Field; + type Gt: Group + ScalarMul + ScalarMulOwned; /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and /// other optimizations. @@ -82,7 +82,7 @@ pub trait Engine: Sized + 'static + Clone { /// to perform pairings. pub trait PairingCurveAffine: CofactorCurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; - type PairingResult: Field; + type PairingResult: Group; /// Perform a pairing fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; @@ -108,7 +108,7 @@ pub trait MultiMillerLoop: Engine { /// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. pub trait MillerLoopResult { /// The extension field that hosts the target group of the pairing. - type Gt: Field; + type Gt: Group; /// This performs a "final exponentiation" routine to convert the result of a Miller /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 6e971622fe..2b56ae3dd8 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,8 +1,8 @@ -use ff::{Endianness, Field, PrimeField}; +use ff::Field; use group::{cofactor::CofactorCurveAffine, Curve, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; -use std::ops::MulAssign; +use std::ops::Mul; use crate::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; @@ -30,12 +30,12 @@ pub fn engine_tests() { let d = E::G2::random(&mut rng).to_affine().into(); assert_eq!( - E::Gt::one(), + E::Gt::identity(), E::multi_miller_loop(&[(&z1, &b)]).final_exponentiation() ); assert_eq!( - E::Gt::one(), + E::Gt::identity(), E::multi_miller_loop(&[(&a, &z2)]).final_exponentiation() ); @@ -85,8 +85,7 @@ fn random_miller_loop_tests() { let ab = E::pairing(&a, &b); let cd = E::pairing(&c, &d); - let mut abcd = ab; - abcd.mul_assign(&cd); + let abcd = ab + &cd; let a = a; let b = b.into(); @@ -121,14 +120,9 @@ fn random_bilinearity_tests() { let acbd = E::pairing(&ac, &bd); let adbc = E::pairing(&ad, &bc); - let mut cd = (c * &d).to_repr(); - ::ReprEndianness::toggle_little_endian(&mut cd); - - use byteorder::ByteOrder; - let mut cd_limbs = [0; 4]; - byteorder::LittleEndian::read_u64_into(cd.as_ref(), &mut cd_limbs); - - let abcd = E::pairing(&a, &b).pow_vartime(cd_limbs); + let ab = E::pairing(&a, &b); + let cd = c * &d; + let abcd = Mul::::mul(ab, cd); assert_eq!(acbd, adbc); assert_eq!(acbd, abcd); From 5ff81049b42ecad8d9e5cf072f6dfde2d5ecf878 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 24 Jun 2020 19:45:27 +1200 Subject: [PATCH 066/210] Pin protobuf 2.14 until our MSRV passes 1.44.1 --- zcash_client_backend/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index c178f087b6..e9d33967e1 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -17,7 +17,7 @@ bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } hex = "0.3" pairing = { version = "0.16", path = "../pairing" } -protobuf = "2" +protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } From c0cf55c127d080a902a664705590ee0a6228164c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 02:16:00 +0000 Subject: [PATCH 067/210] SQLite database structure and initialisation --- Cargo.toml | 1 + zcash_client_sqlite/Cargo.toml | 21 +++ zcash_client_sqlite/README.md | 21 +++ zcash_client_sqlite/src/error.rs | 41 +++++ zcash_client_sqlite/src/init.rs | 286 +++++++++++++++++++++++++++++++ zcash_client_sqlite/src/lib.rs | 33 ++++ 6 files changed, 403 insertions(+) create mode 100644 zcash_client_sqlite/Cargo.toml create mode 100644 zcash_client_sqlite/README.md create mode 100644 zcash_client_sqlite/src/error.rs create mode 100644 zcash_client_sqlite/src/init.rs create mode 100644 zcash_client_sqlite/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index dd595b1bd3..48817cc6c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "group", "pairing", "zcash_client_backend", + "zcash_client_sqlite", "zcash_history", "zcash_primitives", "zcash_proofs", diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml new file mode 100644 index 0000000000..fe15f7b007 --- /dev/null +++ b/zcash_client_sqlite/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "zcash_client_sqlite" +description = "An SQLite-based Zcash light client" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] +rusqlite = { version = "0.20", features = ["bundled"] } +zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } +zcash_primitives = { version = "0.2", path = "../zcash_primitives" } + +[dev-dependencies] +remove_dir_all = "=0.5.2" # tempfile dependency; 0.5.3 bumped the MSRV +tempfile = "3" diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md new file mode 100644 index 0000000000..2c244f8392 --- /dev/null +++ b/zcash_client_sqlite/README.md @@ -0,0 +1,21 @@ +# zcash_client_sqlite + +This library contains APIs that collectively implement a Zcash light client in +an SQLite database. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs new file mode 100644 index 0000000000..514b6f8a3e --- /dev/null +++ b/zcash_client_sqlite/src/error.rs @@ -0,0 +1,41 @@ +use std::error; +use std::fmt; + +#[derive(Debug)] +pub enum ErrorKind { + TableNotEmpty, + Database(rusqlite::Error), +} + +#[derive(Debug)] +pub struct Error(pub(crate) ErrorKind); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self.0 { + ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), + ErrorKind::Database(e) => write!(f, "{}", e), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match &self.0 { + ErrorKind::Database(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: rusqlite::Error) -> Self { + Error(ErrorKind::Database(e)) + } +} + +impl Error { + pub fn kind(&self) -> &ErrorKind { + &self.0 + } +} diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs new file mode 100644 index 0000000000..c5ea7936c4 --- /dev/null +++ b/zcash_client_sqlite/src/init.rs @@ -0,0 +1,286 @@ +//! Functions for initializing the various databases. + +use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::{ + constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + encoding::encode_extended_full_viewing_key, +}; +use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey}; + +use crate::{ + address_from_extfvk, + error::{Error, ErrorKind}, +}; + +/// Sets up the internal structure of the cache database. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::init_cache_database; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_cache = data_file.path(); +/// init_cache_database(&db_cache).unwrap(); +/// ``` +pub fn init_cache_database>(db_cache: P) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + cache.execute( + "CREATE TABLE IF NOT EXISTS compactblocks ( + height INTEGER PRIMARY KEY, + data BLOB NOT NULL + )", + NO_PARAMS, + )?; + Ok(()) +} + +/// Sets up the internal structure of the data database. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::init_data_database; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_data = data_file.path(); +/// init_data_database(&db_data).unwrap(); +/// ``` +pub fn init_data_database>(db_data: P) -> Result<(), Error> { + let data = Connection::open(db_data)?; + data.execute( + "CREATE TABLE IF NOT EXISTS accounts ( + account INTEGER PRIMARY KEY, + extfvk TEXT NOT NULL, + address TEXT NOT NULL + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS blocks ( + height INTEGER PRIMARY KEY, + hash BLOB NOT NULL, + time INTEGER NOT NULL, + sapling_tree BLOB NOT NULL + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS transactions ( + id_tx INTEGER PRIMARY KEY, + txid BLOB NOT NULL UNIQUE, + created TEXT, + block INTEGER, + tx_index INTEGER, + expiry_height INTEGER, + raw BLOB, + FOREIGN KEY (block) REFERENCES blocks(height) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS received_notes ( + id_note INTEGER PRIMARY KEY, + tx INTEGER NOT NULL, + output_index INTEGER NOT NULL, + account INTEGER NOT NULL, + diversifier BLOB NOT NULL, + value INTEGER NOT NULL, + rcm BLOB NOT NULL, + nf BLOB NOT NULL UNIQUE, + is_change BOOLEAN NOT NULL, + memo BLOB, + spent INTEGER, + FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (account) REFERENCES accounts(account), + FOREIGN KEY (spent) REFERENCES transactions(id_tx), + CONSTRAINT tx_output UNIQUE (tx, output_index) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS sapling_witnesses ( + id_witness INTEGER PRIMARY KEY, + note INTEGER NOT NULL, + block INTEGER NOT NULL, + witness BLOB NOT NULL, + FOREIGN KEY (note) REFERENCES received_notes(id_note), + FOREIGN KEY (block) REFERENCES blocks(height), + CONSTRAINT witness_height UNIQUE (note, block) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS sent_notes ( + id_note INTEGER PRIMARY KEY, + tx INTEGER NOT NULL, + output_index INTEGER NOT NULL, + from_account INTEGER NOT NULL, + address TEXT NOT NULL, + value INTEGER NOT NULL, + memo BLOB, + FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (from_account) REFERENCES accounts(account), + CONSTRAINT tx_output UNIQUE (tx, output_index) + )", + NO_PARAMS, + )?; + Ok(()) +} + +/// Initialises the data database with the given [`ExtendedFullViewingKey`]s. +/// +/// The [`ExtendedFullViewingKey`]s are stored internally and used by other APIs such as +/// [`get_address`], [`scan_cached_blocks`], and [`create_to_address`]. `extfvks` **MUST** +/// be arranged in account-order; that is, the [`ExtendedFullViewingKey`] for ZIP 32 +/// account `i` **MUST** be at `extfvks[i]`. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::{init_accounts_table, init_data_database}; +/// use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_data = data_file.path(); +/// init_data_database(&db_data).unwrap(); +/// +/// let extsk = ExtendedSpendingKey::master(&[]); +/// let extfvks = [ExtendedFullViewingKey::from(&extsk)]; +/// init_accounts_table(&db_data, &extfvks).unwrap(); +/// ``` +/// +/// [`get_address`]: crate::query::get_address +/// [`scan_cached_blocks`]: crate::scan::scan_cached_blocks +/// [`create_to_address`]: crate::transact::create_to_address +pub fn init_accounts_table>( + db_data: P, + extfvks: &[ExtendedFullViewingKey], +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + let mut empty_check = data.prepare("SELECT * FROM accounts LIMIT 1")?; + if empty_check.exists(NO_PARAMS)? { + return Err(Error(ErrorKind::TableNotEmpty)); + } + + // Insert accounts atomically + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + for (account, extfvk) in extfvks.iter().enumerate() { + let address = address_from_extfvk(extfvk); + let extfvk = + encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extfvk); + data.execute( + "INSERT INTO accounts (account, extfvk, address) + VALUES (?, ?, ?)", + &[ + (account as u32).to_sql()?, + extfvk.to_sql()?, + address.to_sql()?, + ], + )?; + } + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + +/// Initialises the data database with the given block. +/// +/// This enables a newly-created database to be immediately-usable, without needing to +/// synchronise historic blocks. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::init::init_blocks_table; +/// use zcash_primitives::block::BlockHash; +/// +/// // The block height. +/// let height = 500_000; +/// // The hash of the block header. +/// let hash = BlockHash([0; 32]); +/// // The nTime field from the block header. +/// let time = 12_3456_7890; +/// // The serialized Sapling commitment tree as of this block. +/// // Pre-compute and hard-code, or obtain from a service. +/// let sapling_tree = &[]; +/// +/// init_blocks_table("/path/to/data.db", height, hash, time, sapling_tree); +/// ``` +pub fn init_blocks_table>( + db_data: P, + height: i32, + hash: BlockHash, + time: u32, + sapling_tree: &[u8], +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + let mut empty_check = data.prepare("SELECT * FROM blocks LIMIT 1")?; + if empty_check.exists(NO_PARAMS)? { + return Err(Error(ErrorKind::TableNotEmpty)); + } + + data.execute( + "INSERT INTO blocks (height, hash, time, sapling_tree) + VALUES (?, ?, ?, ?)", + &[ + height.to_sql()?, + hash.0.to_sql()?, + time.to_sql()?, + sapling_tree.to_sql()?, + ], + )?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{init_accounts_table, init_blocks_table, init_data_database}; + + #[test] + fn init_accounts_table_only_works_once() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // We can call the function as many times as we want with no data + init_accounts_table(&db_data, &[]).unwrap(); + init_accounts_table(&db_data, &[]).unwrap(); + + // First call with data should initialise the accounts table + let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master( + &[], + ))]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // Subsequent calls should return an error + init_accounts_table(&db_data, &[]).unwrap_err(); + init_accounts_table(&db_data, &extfvks).unwrap_err(); + } + + #[test] + fn init_blocks_table_only_works_once() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // First call with data should initialise the blocks table + init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); + + // Subsequent calls should return an error + init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err(); + } +} diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs new file mode 100644 index 0000000000..767b582809 --- /dev/null +++ b/zcash_client_sqlite/src/lib.rs @@ -0,0 +1,33 @@ +//! *An SQLite-based Zcash light client.* +//! +//! `zcash_client_backend` contains a set of APIs that collectively implement an +//! SQLite-based light client for the Zcash network. +//! +//! # Design +//! +//! The light client is built around two SQLite databases: +//! +//! - A cache database, used to inform the light client about new [`CompactBlock`]s. It is +//! read-only within all light client APIs *except* for [`init_cache_database`] which +//! can be used to initialize the database. +//! +//! - A data database, where the light client's state is stored. It is read-write within +//! the light client APIs, and **assumed to be read-only outside these APIs**. Callers +//! **MUST NOT** write to the database without using these APIs. Callers **MAY** read +//! the database directly in order to extract information for display to users. +//! +//! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock +//! [`init_cache_database`]: crate::init::init_cache_database + +use zcash_client_backend::{ + constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::encode_payment_address, +}; +use zcash_primitives::zip32::ExtendedFullViewingKey; + +pub mod error; +pub mod init; + +fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { + let addr = extfvk.default_address().unwrap().1; + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr) +} From c8b70e569ced8ec99392e0e89379043d12d33e3b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 02:23:31 +0000 Subject: [PATCH 068/210] zcash_client_sqlite::query::get_address() --- zcash_client_sqlite/src/init.rs | 21 +++++++++++++++++++++ zcash_client_sqlite/src/lib.rs | 1 + zcash_client_sqlite/src/query.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 zcash_client_sqlite/src/query.rs diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index c5ea7936c4..e457ad9304 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -243,12 +243,16 @@ pub fn init_blocks_table>( #[cfg(test)] mod tests { use tempfile::NamedTempFile; + use zcash_client_backend::{ + constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::decode_payment_address, + }; use zcash_primitives::{ block::BlockHash, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; use super::{init_accounts_table, init_blocks_table, init_data_database}; + use crate::query::get_address; #[test] fn init_accounts_table_only_works_once() { @@ -283,4 +287,21 @@ mod tests { // Subsequent calls should return an error init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err(); } + + #[test] + fn init_accounts_table_stores_correct_address() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // The account's address should be in the data DB + let addr = get_address(&db_data, 0).unwrap(); + let pa = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr).unwrap(); + assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1); + } } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 767b582809..32ebc1f9da 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -26,6 +26,7 @@ use zcash_primitives::zip32::ExtendedFullViewingKey; pub mod error; pub mod init; +pub mod query; fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { let addr = extfvk.default_address().unwrap().1; diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs new file mode 100644 index 0000000000..be585d92ff --- /dev/null +++ b/zcash_client_sqlite/src/query.rs @@ -0,0 +1,28 @@ +//! Functions for querying information in the data database. + +use rusqlite::Connection; +use std::path::Path; + +use crate::error::Error; + +/// Returns the address for the account. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_address; +/// +/// let addr = get_address("/path/to/data.db", 0); +/// ``` +pub fn get_address>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let addr = data.query_row( + "SELECT address FROM accounts + WHERE account = ?", + &[account], + |row| row.get(0), + )?; + + Ok(addr) +} From 72dd76e4dbe5e5527733c0a79da9e8bb296be30c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 02:53:38 +0000 Subject: [PATCH 069/210] zcash_client_sqlite::query::{get_balance, get_verified_balance} --- zcash_client_sqlite/src/error.rs | 4 ++ zcash_client_sqlite/src/lib.rs | 31 +++++++++ zcash_client_sqlite/src/query.rs | 107 ++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index 514b6f8a3e..b33b5c2496 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -3,6 +3,8 @@ use std::fmt; #[derive(Debug)] pub enum ErrorKind { + CorruptedData(&'static str), + ScanRequired, TableNotEmpty, Database(rusqlite::Error), } @@ -13,6 +15,8 @@ pub struct Error(pub(crate) ErrorKind); impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.0 { + ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason), + ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), ErrorKind::Database(e) => write!(f, "{}", e), } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 32ebc1f9da..cb52144b42 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -19,6 +19,8 @@ //! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock //! [`init_cache_database`]: crate::init::init_cache_database +use rusqlite::{Connection, NO_PARAMS}; +use std::cmp; use zcash_client_backend::{ constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::encode_payment_address, }; @@ -28,7 +30,36 @@ pub mod error; pub mod init; pub mod query; +const ANCHOR_OFFSET: u32 = 10; + fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { let addr = extfvk.default_address().unwrap().1; encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr) } + +/// Determines the target height for a transaction, and the height from which to +/// select anchors, based on the current synchronised block chain. +fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error::Error> { + data.query_row_and_then( + "SELECT MIN(height), MAX(height) FROM blocks", + NO_PARAMS, + |row| match (row.get::<_, u32>(0), row.get::<_, u32>(1)) { + // If there are no blocks, the query returns NULL. + (Err(rusqlite::Error::InvalidColumnType(_, _, _)), _) + | (_, Err(rusqlite::Error::InvalidColumnType(_, _, _))) => { + Err(error::Error(error::ErrorKind::ScanRequired)) + } + (Err(e), _) | (_, Err(e)) => Err(e.into()), + (Ok(min_height), Ok(max_height)) => { + let target_height = max_height + 1; + + // Select an anchor ANCHOR_OFFSET back from the target block, + // unless that would be before the earliest block we have. + let anchor_height = + cmp::max(target_height.saturating_sub(ANCHOR_OFFSET), min_height); + + Ok((target_height, anchor_height)) + } + }, + ) +} diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index be585d92ff..7806b8da9f 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -2,8 +2,12 @@ use rusqlite::Connection; use std::path::Path; +use zcash_primitives::transaction::components::Amount; -use crate::error::Error; +use crate::{ + error::{Error, ErrorKind}, + get_target_and_anchor_heights, +}; /// Returns the address for the account. /// @@ -26,3 +30,104 @@ pub fn get_address>(db_data: P, account: u32) -> Result>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let balance = data.query_row( + "SELECT SUM(value) FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block IS NOT NULL", + &[account], + |row| row.get(0).or(Ok(0)), + )?; + + match Amount::from_i64(balance) { + Ok(amount) if !amount.is_negative() => Ok(amount), + _ => Err(Error(ErrorKind::CorruptedData( + "Sum of values in received_notes is out of range", + ))), + } +} + +/// Returns the verified balance for the account, which ignores notes that have been +/// received too recently and are not yet deemed spendable. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_verified_balance; +/// +/// let addr = get_verified_balance("/path/to/data.db", 0); +/// ``` +pub fn get_verified_balance>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let (_, anchor_height) = get_target_and_anchor_heights(&data)?; + + let balance = data.query_row( + "SELECT SUM(value) FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block <= ?", + &[account, anchor_height], + |row| row.get(0).or(Ok(0)), + )?; + + match Amount::from_i64(balance) { + Ok(amount) if !amount.is_negative() => Ok(amount), + _ => Err(Error(ErrorKind::CorruptedData( + "Sum of values in received_notes is out of range", + ))), + } +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{get_address, get_balance, get_verified_balance}; + use crate::{ + error::ErrorKind, + init::{init_accounts_table, init_data_database}, + }; + + #[test] + fn empty_database_has_no_balance() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // The account should be empty + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // The account should have no verified balance, as we haven't scanned any blocks + let e = get_verified_balance(db_data, 0).unwrap_err(); + match e.kind() { + ErrorKind::ScanRequired => (), + _ => panic!("Unexpected error: {:?}", e), + } + + // An invalid account has zero balance + assert!(get_address(db_data, 1).is_err()); + assert_eq!(get_balance(db_data, 1).unwrap(), Amount::zero()); + } +} From 3ef03f2f1dbb5eb01d66ddb1feafd2c886299fdb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 03:12:31 +0000 Subject: [PATCH 070/210] zcash_client_sqlite::scan::scan_cached_blocks() --- zcash_client_sqlite/Cargo.toml | 5 + zcash_client_sqlite/src/error.rs | 48 +++ zcash_client_sqlite/src/lib.rs | 172 +++++++++++ zcash_client_sqlite/src/scan.rs | 500 +++++++++++++++++++++++++++++++ 4 files changed, 725 insertions(+) create mode 100644 zcash_client_sqlite/src/scan.rs diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index fe15f7b007..b2994f7a01 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -12,10 +12,15 @@ license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +bech32 = "0.7" +ff = { version = "0.6", path = "../ff" } +protobuf = "2" rusqlite = { version = "0.20", features = ["bundled"] } zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [dev-dependencies] +pairing = { version = "0.16", path = "../pairing" } +rand_core = "0.5.1" remove_dir_all = "=0.5.2" # tempfile dependency; 0.5.3 bumped the MSRV tempfile = "3" diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index b33b5c2496..fcdc1a0793 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -1,12 +1,20 @@ use std::error; use std::fmt; +use zcash_primitives::{sapling::Node, transaction::TxId}; #[derive(Debug)] pub enum ErrorKind { CorruptedData(&'static str), + IncorrectHRPExtFVK, + InvalidHeight(i32, i32), + InvalidNewWitnessAnchor(usize, TxId, i32, Node), + InvalidWitnessAnchor(i64, i32), ScanRequired, TableNotEmpty, + Bech32(bech32::Error), Database(rusqlite::Error), + Io(std::io::Error), + Protobuf(protobuf::ProtobufError), } #[derive(Debug)] @@ -16,9 +24,28 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.0 { ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason), + ErrorKind::IncorrectHRPExtFVK => write!(f, "Incorrect HRP for extfvk"), + ErrorKind::InvalidHeight(expected, actual) => write!( + f, + "Expected height of next CompactBlock to be {}, but was {}", + expected, actual + ), + ErrorKind::InvalidNewWitnessAnchor(output, txid, last_height, anchor) => write!( + f, + "New witness for output {} in tx {} has incorrect anchor after scanning block {}: {:?}", + output, txid, last_height, anchor, + ), + ErrorKind::InvalidWitnessAnchor(id_note, last_height) => write!( + f, + "Witness for note {} has incorrect anchor after scanning block {}", + id_note, last_height + ), ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), + ErrorKind::Bech32(e) => write!(f, "{}", e), ErrorKind::Database(e) => write!(f, "{}", e), + ErrorKind::Io(e) => write!(f, "{}", e), + ErrorKind::Protobuf(e) => write!(f, "{}", e), } } } @@ -26,18 +53,39 @@ impl fmt::Display for Error { impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self.0 { + ErrorKind::Bech32(e) => Some(e), ErrorKind::Database(e) => Some(e), + ErrorKind::Io(e) => Some(e), + ErrorKind::Protobuf(e) => Some(e), _ => None, } } } +impl From for Error { + fn from(e: bech32::Error) -> Self { + Error(ErrorKind::Bech32(e)) + } +} + impl From for Error { fn from(e: rusqlite::Error) -> Self { Error(ErrorKind::Database(e)) } } +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error(ErrorKind::Io(e)) + } +} + +impl From for Error { + fn from(e: protobuf::ProtobufError) -> Self { + Error(ErrorKind::Protobuf(e)) + } +} + impl Error { pub fn kind(&self) -> &ErrorKind { &self.0 diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index cb52144b42..922193a77a 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -29,8 +29,10 @@ use zcash_primitives::zip32::ExtendedFullViewingKey; pub mod error; pub mod init; pub mod query; +pub mod scan; const ANCHOR_OFFSET: u32 = 10; +const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { let addr = extfvk.default_address().unwrap().1; @@ -63,3 +65,173 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error: }, ) } + +#[cfg(test)] +mod tests { + use ff::{Field, PrimeField}; + use pairing::bls12_381::Bls12; + use protobuf::Message; + use rand_core::{OsRng, RngCore}; + use rusqlite::{types::ToSql, Connection}; + use std::path::Path; + use zcash_client_backend::proto::compact_formats::{ + CompactBlock, CompactOutput, CompactSpend, CompactTx, + }; + use zcash_primitives::{ + block::BlockHash, + jubjub::fs::Fs, + note_encryption::{Memo, SaplingNoteEncryption}, + primitives::{Note, PaymentAddress}, + transaction::components::Amount, + zip32::ExtendedFullViewingKey, + JUBJUB, + }; + + /// Create a fake CompactBlock at the given height, containing a single output paying + /// the given address. Returns the CompactBlock and the nullifier for the new note. + pub(crate) fn fake_compact_block( + height: i32, + prev_hash: BlockHash, + extfvk: ExtendedFullViewingKey, + value: Amount, + ) -> (CompactBlock, Vec) { + let to = extfvk.default_address().unwrap().1; + + // Create a fake Note for the account + let mut rng = OsRng; + let note = Note { + g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + pk_d: to.pk_d().clone(), + value: value.into(), + r: Fs::random(&mut rng), + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + to.clone(), + Memo::default(), + &mut rng, + ); + let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); + let mut epk = vec![]; + encryptor.epk().write(&mut epk).unwrap(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + // Create a fake CompactBlock containing the note + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + let mut ctx = CompactTx::new(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.set_hash(txid); + ctx.outputs.push(cout); + let mut cb = CompactBlock::new(); + cb.set_height(height as u64); + cb.hash.resize(32, 0); + rng.fill_bytes(&mut cb.hash); + cb.prevHash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + (cb, note.nf(&extfvk.fvk.vk, 0, &JUBJUB)) + } + + /// Create a fake CompactBlock at the given height, spending a single note from the + /// given address. + pub(crate) fn fake_compact_block_spending( + height: i32, + prev_hash: BlockHash, + (nf, in_value): (Vec, Amount), + extfvk: ExtendedFullViewingKey, + to: PaymentAddress, + value: Amount, + ) -> CompactBlock { + let mut rng = OsRng; + + // Create a fake CompactBlock containing the note + let mut cspend = CompactSpend::new(); + cspend.set_nf(nf); + let mut ctx = CompactTx::new(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.set_hash(txid); + ctx.spends.push(cspend); + + // Create a fake Note for the payment + ctx.outputs.push({ + let note = Note { + g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + pk_d: to.pk_d().clone(), + value: value.into(), + r: Fs::random(&mut rng), + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + to, + Memo::default(), + &mut rng, + ); + let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); + let mut epk = vec![]; + encryptor.epk().write(&mut epk).unwrap(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + cout + }); + + // Create a fake Note for the change + ctx.outputs.push({ + let change_addr = extfvk.default_address().unwrap().1; + let note = Note { + g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), + pk_d: change_addr.pk_d().clone(), + value: (in_value - value).into(), + r: Fs::random(&mut rng), + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + change_addr, + Memo::default(), + &mut rng, + ); + let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); + let mut epk = vec![]; + encryptor.epk().write(&mut epk).unwrap(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + cout + }); + + let mut cb = CompactBlock::new(); + cb.set_height(height as u64); + cb.hash.resize(32, 0); + rng.fill_bytes(&mut cb.hash); + cb.prevHash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + cb + } + + /// Insert a fake CompactBlock into the cache DB. + pub(crate) fn insert_into_cache>(db_cache: P, cb: &CompactBlock) { + let cb_bytes = cb.write_to_bytes().unwrap(); + let cache = Connection::open(&db_cache).unwrap(); + cache + .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") + .unwrap() + .execute(&[ + (cb.height as i32).to_sql().unwrap(), + cb_bytes.to_sql().unwrap(), + ]) + .unwrap(); + } +} diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs new file mode 100644 index 0000000000..01028d8b6d --- /dev/null +++ b/zcash_client_sqlite/src/scan.rs @@ -0,0 +1,500 @@ +//! Functions for scanning the chain and extracting relevant information. + +use ff::PrimeField; +use protobuf::parse_from_bytes; +use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::{ + constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock, + welding_rig::scan_block, +}; +use zcash_primitives::{ + merkle_tree::{CommitmentTree, IncrementalWitness}, + sapling::Node, + JUBJUB, +}; + +use crate::{ + error::{Error, ErrorKind}, + SAPLING_ACTIVATION_HEIGHT, +}; + +struct CompactBlockRow { + height: i32, + data: Vec, +} + +#[derive(Clone)] +struct WitnessRow { + id_note: i64, + witness: IncrementalWitness, +} + +/// Scans new blocks added to the cache for any transactions received by the tracked +/// accounts. +/// +/// This function pays attention only to cached blocks with heights greater than the +/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified +/// against previously-scanned blocks. In particular, this function **assumes** that the +/// caller is handling rollbacks. +/// +/// For brand-new light client databases, this function starts scanning from the Sapling +/// activation height. This height can be fast-forwarded to a more recent block by calling +/// [`init_blocks_table`] before this function. +/// +/// Scanned blocks are required to be height-sequential. If a block is missing from the +/// cache, an error will be returned with kind [`ErrorKind::InvalidHeight`]. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::scan::scan_cached_blocks; +/// +/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db"); +/// ``` +/// +/// [`init_blocks_table`]: crate::init::init_blocks_table +pub fn scan_cached_blocks, Q: AsRef>( + db_cache: P, + db_data: Q, +) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use sapling activation height to select all cached CompactBlocks. + let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + + // Fetch the CompactBlocks we need to scan + let mut stmt_blocks = cache + .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC")?; + let rows = stmt_blocks.query_map(&[last_height], |row| { + Ok(CompactBlockRow { + height: row.get(0)?, + data: row.get(1)?, + }) + })?; + + // Fetch the ExtendedFullViewingKeys we are tracking + let mut stmt_fetch_accounts = + data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; + let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { + row.get(0).map(|extfvk: String| { + decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + }) + })?; + // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. + let extfvks: Vec<_> = extfvks + .collect::, _>, _>>()?? + .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; + + // Get the most recent CommitmentTree + let mut stmt_fetch_tree = data.prepare("SELECT sapling_tree FROM blocks WHERE height = ?")?; + let mut tree = stmt_fetch_tree + .query_row(&[last_height], |row| { + row.get(0).map(|data: Vec<_>| { + CommitmentTree::read(&data[..]).unwrap_or_else(|_| CommitmentTree::new()) + }) + }) + .unwrap_or_else(|_| CommitmentTree::new()); + + // Get most recent incremental witnesses for the notes we are tracking + let mut stmt_fetch_witnesses = + data.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?; + let witnesses = stmt_fetch_witnesses.query_map(&[last_height], |row| { + let id_note = row.get(0)?; + let data: Vec<_> = row.get(1)?; + Ok(IncrementalWitness::read(&data[..]).map(|witness| WitnessRow { id_note, witness })) + })?; + let mut witnesses: Vec<_> = witnesses.collect::, _>>()??; + + // Get the nullifiers for the notes we are tracking + let mut stmt_fetch_nullifiers = + data.prepare("SELECT id_note, nf, account FROM received_notes WHERE spent IS NULL")?; + let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| { + let nf: Vec<_> = row.get(1)?; + let account: i64 = row.get(2)?; + Ok((nf, account as usize)) + })?; + let mut nullifiers: Vec<_> = nullifiers.collect::>()?; + + // Prepare per-block SQL statements + let mut stmt_insert_block = data.prepare( + "INSERT INTO blocks (height, hash, time, sapling_tree) + VALUES (?, ?, ?, ?)", + )?; + let mut stmt_update_tx = data.prepare( + "UPDATE transactions + SET block = ?, tx_index = ? WHERE txid = ?", + )?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, block, tx_index) + VALUES (?, ?, ?)", + )?; + let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; + let mut stmt_mark_spent_note = + data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; + let mut stmt_insert_note = data.prepare( + "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + )?; + let mut stmt_insert_witness = data.prepare( + "INSERT INTO sapling_witnesses (note, block, witness) + VALUES (?, ?, ?)", + )?; + let mut stmt_prune_witnesses = data.prepare("DELETE FROM sapling_witnesses WHERE block < ?")?; + let mut stmt_update_expired = data.prepare( + "UPDATE received_notes SET spent = NULL WHERE EXISTS ( + SELECT id_tx FROM transactions + WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ? + )", + )?; + + for row in rows { + let row = row?; + + // Start an SQL transaction for this block. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Scanned blocks MUST be height-sequential. + if row.height != (last_height + 1) { + return Err(Error(ErrorKind::InvalidHeight(last_height + 1, row.height))); + } + last_height = row.height; + + let block: CompactBlock = parse_from_bytes(&row.data)?; + let block_hash = block.hash.clone(); + let block_time = block.time; + + let txs = { + let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); + let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect(); + scan_block( + block, + &extfvks[..], + &nf_refs, + &mut tree, + &mut witness_refs[..], + ) + }; + + // Enforce that all roots match. This is slow, so only include in debug builds. + #[cfg(debug_assertions)] + { + let cur_root = tree.root(); + for row in &witnesses { + if row.witness.root() != cur_root { + return Err(Error(ErrorKind::InvalidWitnessAnchor( + row.id_note, + last_height, + ))); + } + } + for tx in &txs { + for output in tx.shielded_outputs.iter() { + if output.witness.root() != cur_root { + return Err(Error(ErrorKind::InvalidNewWitnessAnchor( + output.index, + tx.txid, + last_height, + output.witness.root(), + ))); + } + } + } + } + + // Insert the block into the database. + let mut encoded_tree = Vec::new(); + tree.write(&mut encoded_tree) + .expect("Should be able to write to a Vec"); + stmt_insert_block.execute(&[ + row.height.to_sql()?, + block_hash.to_sql()?, + block_time.to_sql()?, + encoded_tree.to_sql()?, + ])?; + + for tx in txs { + // First try update an existing transaction in the database. + let txid = tx.txid.0.to_vec(); + let tx_row = if stmt_update_tx.execute(&[ + row.height.to_sql()?, + (tx.index as i64).to_sql()?, + txid.to_sql()?, + ])? == 0 + { + // It isn't there, so insert our transaction into the database. + stmt_insert_tx.execute(&[ + txid.to_sql()?, + row.height.to_sql()?, + (tx.index as i64).to_sql()?, + ])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_tx.query_row(&[txid], |row| row.get(0))? + }; + + // Mark notes as spent and remove them from the scanning cache + for spend in &tx.shielded_spends { + stmt_mark_spent_note.execute(&[tx_row.to_sql()?, spend.nf.to_sql()?])?; + } + nullifiers = nullifiers + .into_iter() + .filter(|(nf, _acc)| { + tx.shielded_spends + .iter() + .find(|spend| &spend.nf == nf) + .is_none() + }) + .collect(); + + for output in tx.shielded_outputs { + let rcm = output.note.r.to_repr(); + let nf = output.note.nf( + &extfvks[output.account].fvk.vk, + output.witness.position() as u64, + &JUBJUB, + ); + + // Insert received note into the database. + // Assumptions: + // - A transaction will not contain more than 2^63 shielded outputs. + // - A note value will never exceed 2^63 zatoshis. + stmt_insert_note.execute(&[ + tx_row.to_sql()?, + (output.index as i64).to_sql()?, + (output.account as i64).to_sql()?, + output.to.diversifier().0.to_sql()?, + (output.note.value as i64).to_sql()?, + rcm.as_ref().to_sql()?, + nf.to_sql()?, + output.is_change.to_sql()?, + ])?; + let note_row = data.last_insert_rowid(); + + // Save witness for note. + witnesses.push(WitnessRow { + id_note: note_row, + witness: output.witness, + }); + + // Cache nullifier for note (to detect subsequent spends in this scan). + nullifiers.push((nf, output.account)); + } + } + + // Insert current witnesses into the database. + let mut encoded = Vec::new(); + for witness_row in witnesses.iter() { + encoded.clear(); + witness_row + .witness + .write(&mut encoded) + .expect("Should be able to write to a Vec"); + stmt_insert_witness.execute(&[ + witness_row.id_note.to_sql()?, + last_height.to_sql()?, + encoded.to_sql()?, + ])?; + } + + // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). + stmt_prune_witnesses.execute(&[last_height - 100])?; + + // Update now-expired transactions that didn't get mined. + stmt_update_expired.execute(&[last_height])?; + + // Commit the SQL transaction, writing this block's data atomically. + data.execute("COMMIT", NO_PARAMS)?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::scan_cached_blocks; + use crate::{ + init::{init_accounts_table, init_cache_database, init_data_database}, + query::get_balance, + tests::{fake_compact_block, fake_compact_block_spending, insert_into_cache}, + SAPLING_ACTIVATION_HEIGHT, + }; + + #[test] + fn scan_cached_blocks_requires_sequential_blocks() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create a block with height SAPLING_ACTIVATION_HEIGHT + let value = Amount::from_u64(50000).unwrap(); + let (cb1, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb1); + scan_cached_blocks(db_cache, db_data).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb1.hash(), + extfvk.clone(), + value, + ); + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + cb2.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb3); + match scan_cached_blocks(db_cache, db_data) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + format!( + "Expected height of next CompactBlock to be {}, but was {}", + SAPLING_ACTIVATION_HEIGHT + 1, + SAPLING_ACTIVATION_HEIGHT + 2 + ) + ), + } + + // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both + insert_into_cache(db_cache, &cb2); + scan_cached_blocks(db_cache, db_data).unwrap(); + assert_eq!( + get_balance(db_data, 0).unwrap(), + Amount::from_u64(150_000).unwrap() + ); + } + + #[test] + fn scan_cached_blocks_finds_received_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create a fake CompactBlock sending value to the address + let value = Amount::from_u64(5).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Account balance should reflect the received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Create a second fake CompactBlock sending more value to the address + let value2 = Amount::from_u64(7).unwrap(); + let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + insert_into_cache(db_cache, &cb2); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Account balance should reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + } + + #[test] + fn scan_cached_blocks_finds_change_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create a fake CompactBlock sending value to the address + let value = Amount::from_u64(5).unwrap(); + let (cb, nf) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Account balance should reflect the received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Create a second fake CompactBlock spending value from the address + let extsk2 = ExtendedSpendingKey::master(&[0]); + let to2 = extsk2.default_address().unwrap().1; + let value2 = Amount::from_u64(2).unwrap(); + insert_into_cache( + db_cache, + &fake_compact_block_spending( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + (nf, value), + extfvk, + to2, + value2, + ), + ); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Account balance should equal the change + assert_eq!(get_balance(db_data, 0).unwrap(), value - value2); + } +} From 34725df086d7f73816434b9d4dc20b76b5f81b69 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 03:20:32 +0000 Subject: [PATCH 071/210] zcash_client_sqlite::transact::create_to_address() --- zcash_client_sqlite/Cargo.toml | 4 +- zcash_client_sqlite/src/error.rs | 29 +- zcash_client_sqlite/src/lib.rs | 1 + zcash_client_sqlite/src/transact.rs | 669 ++++++++++++++++++++ zcash_primitives/src/transaction/builder.rs | 22 + 5 files changed, 723 insertions(+), 2 deletions(-) create mode 100644 zcash_client_sqlite/src/transact.rs diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index b2994f7a01..cc9d7ae62e 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -14,13 +14,15 @@ edition = "2018" [dependencies] bech32 = "0.7" ff = { version = "0.6", path = "../ff" } +pairing = { version = "0.16", path = "../pairing" } protobuf = "2" rusqlite = { version = "0.20", features = ["bundled"] } +time = "0.1" zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [dev-dependencies] -pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" remove_dir_all = "=0.5.2" # tempfile dependency; 0.5.3 bumped the MSRV tempfile = "3" +zcash_proofs = { version = "0.2", path = "../zcash_proofs" } diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index fcdc1a0793..52ff230a17 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -1,17 +1,25 @@ use std::error; use std::fmt; -use zcash_primitives::{sapling::Node, transaction::TxId}; +use zcash_primitives::{ + sapling::Node, + transaction::{builder, TxId}, +}; #[derive(Debug)] pub enum ErrorKind { CorruptedData(&'static str), IncorrectHRPExtFVK, + InsufficientBalance(u64, u64), + InvalidExtSK(u32), InvalidHeight(i32, i32), + InvalidMemo(std::str::Utf8Error), InvalidNewWitnessAnchor(usize, TxId, i32, Node), + InvalidNote, InvalidWitnessAnchor(i64, i32), ScanRequired, TableNotEmpty, Bech32(bech32::Error), + Builder(builder::Error), Database(rusqlite::Error), Io(std::io::Error), Protobuf(protobuf::ProtobufError), @@ -25,16 +33,26 @@ impl fmt::Display for Error { match &self.0 { ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason), ErrorKind::IncorrectHRPExtFVK => write!(f, "Incorrect HRP for extfvk"), + ErrorKind::InsufficientBalance(have, need) => write!( + f, + "Insufficient balance (have {}, need {} including fee)", + have, need + ), + ErrorKind::InvalidExtSK(account) => { + write!(f, "Incorrect ExtendedSpendingKey for account {}", account) + } ErrorKind::InvalidHeight(expected, actual) => write!( f, "Expected height of next CompactBlock to be {}, but was {}", expected, actual ), + ErrorKind::InvalidMemo(e) => write!(f, "{}", e), ErrorKind::InvalidNewWitnessAnchor(output, txid, last_height, anchor) => write!( f, "New witness for output {} in tx {} has incorrect anchor after scanning block {}: {:?}", output, txid, last_height, anchor, ), + ErrorKind::InvalidNote => write!(f, "Invalid note"), ErrorKind::InvalidWitnessAnchor(id_note, last_height) => write!( f, "Witness for note {} has incorrect anchor after scanning block {}", @@ -43,6 +61,7 @@ impl fmt::Display for Error { ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), ErrorKind::Bech32(e) => write!(f, "{}", e), + ErrorKind::Builder(e) => write!(f, "{:?}", e), ErrorKind::Database(e) => write!(f, "{}", e), ErrorKind::Io(e) => write!(f, "{}", e), ErrorKind::Protobuf(e) => write!(f, "{}", e), @@ -53,7 +72,9 @@ impl fmt::Display for Error { impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self.0 { + ErrorKind::InvalidMemo(e) => Some(e), ErrorKind::Bech32(e) => Some(e), + ErrorKind::Builder(e) => Some(e), ErrorKind::Database(e) => Some(e), ErrorKind::Io(e) => Some(e), ErrorKind::Protobuf(e) => Some(e), @@ -68,6 +89,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: builder::Error) -> Self { + Error(ErrorKind::Builder(e)) + } +} + impl From for Error { fn from(e: rusqlite::Error) -> Self { Error(ErrorKind::Database(e)) diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 922193a77a..df02439931 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -30,6 +30,7 @@ pub mod error; pub mod init; pub mod query; pub mod scan; +pub mod transact; const ANCHOR_OFFSET: u32 = 10; const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs new file mode 100644 index 0000000000..55b84f90fd --- /dev/null +++ b/zcash_client_sqlite/src/transact.rs @@ -0,0 +1,669 @@ +//! Functions for creating transactions. + +use ff::PrimeField; +use pairing::bls12_381::Bls12; +use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use std::convert::TryInto; +use std::path::Path; +use zcash_client_backend::{ + constants::testnet::{HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS}, + encoding::{encode_extended_full_viewing_key, encode_payment_address}, +}; +use zcash_primitives::{ + consensus, + jubjub::fs::{Fs, FsRepr}, + merkle_tree::{IncrementalWitness, MerklePath}, + note_encryption::Memo, + primitives::{Diversifier, Note, PaymentAddress}, + prover::TxProver, + sapling::Node, + transaction::{ + builder::Builder, + components::{amount::DEFAULT_FEE, Amount}, + }, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + JUBJUB, +}; + +use crate::{ + error::{Error, ErrorKind}, + get_target_and_anchor_heights, +}; + +struct SelectedNoteRow { + diversifier: Diversifier, + note: Note, + merkle_path: MerklePath, +} + +/// Creates a transaction paying the specified address from the given account. +/// +/// Returns the row index of the newly-created transaction in the `transactions` table +/// within the data database. The caller can read the raw transaction bytes from the `raw` +/// column in order to broadcast the transaction to the network. +/// +/// Do not call this multiple times in parallel, or you will generate transactions that +/// double-spend the same notes. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::COIN_TYPE, +/// keys::spending_key, +/// }; +/// use zcash_client_sqlite::transact::create_to_address; +/// use zcash_primitives::{consensus, transaction::components::Amount}; +/// use zcash_proofs::prover::LocalTxProver; +/// +/// let tx_prover = match LocalTxProver::with_default_location() { +/// Some(tx_prover) => tx_prover, +/// None => { +/// panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); +/// } +/// }; +/// +/// let account = 0; +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); +/// let to = extsk.default_address().unwrap().1; +/// match create_to_address( +/// "/path/to/data.db", +/// consensus::BranchId::Sapling, +/// tx_prover, +/// (account, &extsk), +/// &to, +/// Amount::from_u64(1).unwrap(), +/// None, +/// ) { +/// Ok(tx_row) => (), +/// Err(e) => (), +/// } +/// ``` +pub fn create_to_address>( + db_data: P, + consensus_branch_id: consensus::BranchId, + prover: impl TxProver, + (account, extsk): (u32, &ExtendedSpendingKey), + to: &PaymentAddress, + value: Amount, + memo: Option, +) -> Result { + let data = Connection::open(db_data)?; + + // Check that the ExtendedSpendingKey we have been given corresponds to the + // ExtendedFullViewingKey for the account we are spending from. + let extfvk = ExtendedFullViewingKey::from(extsk); + if !data + .prepare("SELECT * FROM accounts WHERE account = ? AND extfvk = ?")? + .exists(&[ + account.to_sql()?, + encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + .to_sql()?, + ])? + { + return Err(Error(ErrorKind::InvalidExtSK(account))); + } + let ovk = extfvk.fvk.ovk; + + // Target the next block, assuming we are up-to-date. + let (height, anchor_height) = { + let (target_height, anchor_height) = get_target_and_anchor_heights(&data)?; + (target_height, i64::from(anchor_height)) + }; + + // The goal of this SQL statement is to select the oldest notes until the required + // value has been reached, and then fetch the witnesses at the desired height for the + // selected notes. This is achieved in several steps: + // + // 1) Use a window function to create a view of all notes, ordered from oldest to + // newest, with an additional column containing a running sum: + // - Unspent notes accumulate the values of all unspent notes in that note's + // account, up to itself. + // - Spent notes accumulate the values of all notes in the transaction they were + // spent in, up to itself. + // + // 2) Select all unspent notes in the desired account, along with their running sum. + // + // 3) Select all notes for which the running sum was less than the required value, as + // well as a single note for which the sum was greater than or equal to the + // required value, bringing the sum of all selected notes across the threshold. + // + // 4) Match the selected notes against the witnesses at the desired height. + let target_value = i64::from(value + DEFAULT_FEE); + let mut stmt_select_notes = data.prepare( + "WITH selected AS ( + WITH eligible AS ( + SELECT id_note, diversifier, value, rcm, + SUM(value) OVER + (PARTITION BY account, spent ORDER BY id_note) AS so_far + FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block <= ? + ) + SELECT * FROM eligible WHERE so_far < ? + UNION + SELECT * FROM (SELECT * FROM eligible WHERE so_far >= ? LIMIT 1) + ), witnesses AS ( + SELECT note, witness FROM sapling_witnesses + WHERE block = ? + ) + SELECT selected.diversifier, selected.value, selected.rcm, witnesses.witness + FROM selected + INNER JOIN witnesses ON selected.id_note = witnesses.note", + )?; + + // Select notes + let notes = stmt_select_notes.query_and_then::<_, Error, _, _>( + &[ + i64::from(account), + anchor_height, + target_value, + target_value, + anchor_height, + ], + |row| { + let diversifier = { + let d: Vec<_> = row.get(0)?; + if d.len() != 11 { + return Err(Error(ErrorKind::CorruptedData( + "Invalid diversifier length", + ))); + } + let mut tmp = [0; 11]; + tmp.copy_from_slice(&d); + Diversifier(tmp) + }; + + let note_value: i64 = row.get(1)?; + + let rcm = { + let d: Vec<_> = row.get(2)?; + let tmp = FsRepr( + d[..] + .try_into() + .map_err(|_| Error(ErrorKind::InvalidNote))?, + ); + Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))? + }; + + let from = extfvk + .fvk + .vk + .to_payment_address(diversifier, &JUBJUB) + .unwrap(); + let note = from.create_note(note_value as u64, rcm, &JUBJUB).unwrap(); + + let merkle_path = { + let d: Vec<_> = row.get(3)?; + IncrementalWitness::read(&d[..])? + .path() + .expect("the tree is not empty") + }; + + Ok(SelectedNoteRow { + diversifier, + note, + merkle_path, + }) + }, + )?; + let notes: Vec = notes.collect::>()?; + + // Confirm we were able to select sufficient value + let selected_value = notes + .iter() + .fold(0, |acc, selected| acc + selected.note.value); + if selected_value < target_value as u64 { + return Err(Error(ErrorKind::InsufficientBalance( + selected_value, + target_value as u64, + ))); + } + + // Create the transaction + let mut builder = Builder::new(height); + for selected in notes { + builder.add_sapling_spend( + extsk.clone(), + selected.diversifier, + selected.note, + selected.merkle_path, + )?; + } + builder.add_sapling_output(ovk, to.clone(), value, memo.clone())?; + let (tx, tx_metadata) = builder.build(consensus_branch_id, &prover)?; + // We only called add_sapling_output() once. + let output_index = match tx_metadata.output_index(0) { + Some(idx) => idx as i64, + None => panic!("Output 0 should exist in the transaction"), + }; + let created = time::get_time(); + + // Update the database atomically, to ensure the result is internally consistent. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Save the transaction in the database. + let mut raw_tx = vec![]; + tx.write(&mut raw_tx)?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, created, expiry_height, raw) + VALUES (?, ?, ?, ?)", + )?; + stmt_insert_tx.execute(&[ + tx.txid().0.to_sql()?, + created.to_sql()?, + tx.expiry_height.to_sql()?, + raw_tx.to_sql()?, + ])?; + let id_tx = data.last_insert_rowid(); + + // Mark notes as spent. + // + // This locks the notes so they aren't selected again by a subsequent call to + // create_to_address() before this transaction has been mined (at which point the notes + // get re-marked as spent). + // + // Assumes that create_to_address() will never be called in parallel, which is a + // reasonable assumption for a light client such as a mobile phone. + let mut stmt_mark_spent_note = + data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; + for spend in &tx.shielded_spends { + stmt_mark_spent_note.execute(&[id_tx.to_sql()?, spend.nullifier.to_sql()?])?; + } + + // Save the sent note in the database. + let to_str = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, to); + if let Some(memo) = memo { + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) + VALUES (?, ?, ?, ?, ?, ?)", + )?; + stmt_insert_sent_note.execute(&[ + id_tx.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + i64::from(value).to_sql()?, + memo.as_bytes().to_sql()?, + ])?; + } else { + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value) + VALUES (?, ?, ?, ?, ?)", + )?; + stmt_insert_sent_note.execute(&[ + id_tx.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + i64::from(value).to_sql()?, + ])?; + } + + data.execute("COMMIT", NO_PARAMS)?; + + // Return the row number of the transaction, so the caller can fetch it for sending. + Ok(id_tx) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + consensus, + prover::TxProver, + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + use zcash_proofs::prover::LocalTxProver; + + use super::create_to_address; + use crate::{ + init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database}, + query::{get_balance, get_verified_balance}, + scan::scan_cached_blocks, + tests::{fake_compact_block, insert_into_cache}, + SAPLING_ACTIVATION_HEIGHT, + }; + + fn test_prover() -> impl TxProver { + match LocalTxProver::with_default_location() { + Some(tx_prover) => tx_prover, + None => { + panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); + } + } + } + + #[test] + fn create_to_address_fails_on_incorrect_extsk() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add two accounts to the wallet + let extsk0 = ExtendedSpendingKey::master(&[]); + let extsk1 = ExtendedSpendingKey::master(&[0]); + let extfvks = [ + ExtendedFullViewingKey::from(&extsk0), + ExtendedFullViewingKey::from(&extsk1), + ]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk0.default_address().unwrap().1; + + // Invalid extsk for the given account should cause an error + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk1), + &to, + Amount::from_u64(1).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 0"), + } + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (1, &extsk0), + &to, + Amount::from_u64(1).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 1"), + } + } + + #[test] + fn create_to_address_fails_with_no_blocks() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk.default_address().unwrap().1; + + // We cannot do anything if we aren't synchronised + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(1).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Must scan blocks first"), + } + } + + #[test] + fn create_to_address_fails_on_insufficient_balance() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk.default_address().unwrap().1; + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // We cannot spend anything + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(1).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 10001 including fee)" + ), + } + } + + #[test] + fn create_to_address_fails_on_unverified_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Verified balance matches total balance + assert_eq!(get_balance(db_data, 0).unwrap(), value); + assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); + + // Add more funds to the wallet in a second note + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Verified balance does not include the second note + assert_eq!(get_balance(db_data, 0).unwrap(), value + value); + assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); + + // Spend fails because there are insufficient verified notes + let extsk2 = ExtendedSpendingKey::master(&[]); + let to = extsk2.default_address().unwrap().1; + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 50000, need 80000 including fee)" + ), + } + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second + // note is verified + for i in 2..10 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Second spend still fails + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 50000, need 80000 including fee)" + ), + } + + // Mine block 11 so that the second note becomes verified + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 10, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Second spend should now succeed + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + ) + .unwrap(); + } + + #[test] + fn create_to_address_fails_on_locked_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Send some of the funds to another address + let extsk2 = ExtendedSpendingKey::master(&[]); + let to = extsk2.default_address().unwrap().1; + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(15000).unwrap(), + None, + ) + .unwrap(); + + // A second spend fails because there are no usable notes + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 12000 including fee)" + ), + } + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 21 (that don't send us funds) + // until just before the first transaction expires + for i in 1..22 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Second spend still fails + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 12000 including fee)" + ), + } + + // Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 22, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Second spend should now succeed + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + ) + .unwrap(); + } +} diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 18510a0c27..d554e27a6e 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -8,6 +8,8 @@ use crate::{ use ff::Field; use pairing::bls12_381::{Bls12, Fr}; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; +use std::error; +use std::fmt; use crate::{ consensus, @@ -48,6 +50,26 @@ pub enum Error { SpendProof, } +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::AnchorMismatch => { + write!(f, "Anchor mismatch (anchors for all spends must be equal)") + } + Error::BindingSig => write!(f, "Failed to create bindingSig"), + Error::ChangeIsNegative(amount) => { + write!(f, "Change is negative ({:?} zatoshis)", amount) + } + Error::InvalidAddress => write!(f, "Invalid address"), + Error::InvalidAmount => write!(f, "Invalid amount"), + Error::NoChangeAddress => write!(f, "No change address specified or discoverable"), + Error::SpendProof => write!(f, "Failed to create Sapling spend proof"), + } + } +} + +impl error::Error for Error {} + struct SpendDescriptionInfo { extsk: ExtendedSpendingKey, diversifier: Diversifier, From 02324fb7679cd00dcc269eb7c49b57405cb35a51 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 9 Mar 2019 03:22:35 +0000 Subject: [PATCH 072/210] zcash_client_sqlite::query::get_*_memo_as_utf8() --- zcash_client_sqlite/src/query.rs | 70 +++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index 7806b8da9f..656e6924fd 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -2,7 +2,7 @@ use rusqlite::Connection; use std::path::Path; -use zcash_primitives::transaction::components::Amount; +use zcash_primitives::{note_encryption::Memo, transaction::components::Amount}; use crate::{ error::{Error, ErrorKind}, @@ -91,6 +91,74 @@ pub fn get_verified_balance>(db_data: P, account: u32) -> Result< } } +/// Returns the memo for a received note, if it is known and a valid UTF-8 string. +/// +/// The note is identified by its row index in the `received_notes` table within the data +/// database. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_received_memo_as_utf8; +/// +/// let memo = get_received_memo_as_utf8("/path/to/data.db", 27); +pub fn get_received_memo_as_utf8>( + db_data: P, + id_note: i64, +) -> Result, Error> { + let data = Connection::open(db_data)?; + + let memo: Vec<_> = data.query_row( + "SELECT memo FROM received_notes + WHERE id_note = ?", + &[id_note], + |row| row.get(0), + )?; + + match Memo::from_bytes(&memo) { + Some(memo) => match memo.to_utf8() { + Some(Ok(res)) => Ok(Some(res)), + Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), + None => Ok(None), + }, + None => Ok(None), + } +} + +/// Returns the memo for a sent note, if it is known and a valid UTF-8 string. +/// +/// The note is identified by its row index in the `sent_notes` table within the data +/// database. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_sent_memo_as_utf8; +/// +/// let memo = get_sent_memo_as_utf8("/path/to/data.db", 12); +pub fn get_sent_memo_as_utf8>( + db_data: P, + id_note: i64, +) -> Result, Error> { + let data = Connection::open(db_data)?; + + let memo: Vec<_> = data.query_row( + "SELECT memo FROM sent_notes + WHERE id_note = ?", + &[id_note], + |row| row.get(0), + )?; + + match Memo::from_bytes(&memo) { + Some(memo) => match memo.to_utf8() { + Some(Ok(res)) => Ok(Some(res)), + Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), + None => Ok(None), + }, + None => Ok(None), + } +} + #[cfg(test)] mod tests { use tempfile::NamedTempFile; From a2de5d70283712892ed8e790f382c2fab0623a8b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 13 Mar 2019 05:46:17 +0000 Subject: [PATCH 073/210] Add security disclaimer to README --- zcash_client_sqlite/README.md | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md index 2c244f8392..b5fc4f182e 100644 --- a/zcash_client_sqlite/README.md +++ b/zcash_client_sqlite/README.md @@ -1,3 +1,43 @@ +# Security Disclaimer + +#### :warning: WARNING: This is an *early preview* + +---- + +In the spirit of transparency, we provide this as a window into what we are actively +developing. This is an alpha build, not yet intended for 3rd party use. Please be advised +of the following: + +* 🛑 This code currently is not audited. 🛑 +* ❌ This is a public, active branch with **no support**. +* ❌ The code **does not have** documentation that is reviewed and approved by our Documentation team. +* ❌ The code **does not have** adequate unit tests, acceptance tests and stress tests. +* ❌ The code **does not have** automated tests that use the officially supported CI system. +* ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. +* :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this. +* :heavy_check_mark: The library **is not** majorly broken in some way. +* ❌ The library **only runs** on testnet. +* ❌ The library **does not run** on mainnet or regtest. +* ❌ We **are actively rebasing** this branch and adding features where/when needed. +* ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.). +* :heavy_check_mark: There is a product manager for this library. +* :heavy_check_mark: Electric Coin Company maintains the library as we discover bugs and do network upgrades/minor releases. +* :heavy_check_mark: Users can expect to get a response within a few weeks after submitting an issue. +* ❌ The User Support team **has not yet been briefed** on the features provided to users and the functionality of the associated test-framework. +* ❌ The code is **not fully-documented**. + + +### 🛑 Use of this code may lead to a loss of funds 🛑 + +Use of this code in its current form or with modifications may lead to loss of funds, loss +of "expected" privacy, or denial of service for a large portion of users, or a bug which +could leverage any of those kinds of attacks (especially a "0 day" where we suspect few +people know about the vulnerability). + +### :eyes: At this time, this is for preview purposes only. :eyes: + +---- + # zcash_client_sqlite This library contains APIs that collectively implement a Zcash light client in From f0ce0c553088ffd85aab74c121aee694790d1970 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 8 Apr 2019 12:24:49 +0100 Subject: [PATCH 074/210] Add mainnet support to zcash_client_sqlite via a feature flag --- zcash_client_sqlite/Cargo.toml | 3 +++ zcash_client_sqlite/README.md | 3 +-- zcash_client_sqlite/src/init.rs | 12 ++++-------- zcash_client_sqlite/src/lib.rs | 24 +++++++++++++++++++++--- zcash_client_sqlite/src/scan.rs | 3 +-- zcash_client_sqlite/src/transact.rs | 8 +++----- 6 files changed, 33 insertions(+), 20 deletions(-) diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index cc9d7ae62e..92df0fad88 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -26,3 +26,6 @@ rand_core = "0.5.1" remove_dir_all = "=0.5.2" # tempfile dependency; 0.5.3 bumped the MSRV tempfile = "3" zcash_proofs = { version = "0.2", path = "../zcash_proofs" } + +[features] +mainnet = [] diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md index b5fc4f182e..d73e3fe3b4 100644 --- a/zcash_client_sqlite/README.md +++ b/zcash_client_sqlite/README.md @@ -16,8 +16,7 @@ of the following: * ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. * :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this. * :heavy_check_mark: The library **is not** majorly broken in some way. -* ❌ The library **only runs** on testnet. -* ❌ The library **does not run** on mainnet or regtest. +* :heavy_check_mark: The library **does run** on mainnet and testnet. * ❌ We **are actively rebasing** this branch and adding features where/when needed. * ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.). * :heavy_check_mark: There is a product manager for this library. diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index e457ad9304..e6eeaffecd 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -2,15 +2,13 @@ use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; -use zcash_client_backend::{ - constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - encoding::encode_extended_full_viewing_key, -}; +use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey}; use crate::{ address_from_extfvk, error::{Error, ErrorKind}, + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, }; /// Sets up the internal structure of the cache database. @@ -243,16 +241,14 @@ pub fn init_blocks_table>( #[cfg(test)] mod tests { use tempfile::NamedTempFile; - use zcash_client_backend::{ - constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::decode_payment_address, - }; + use zcash_client_backend::encoding::decode_payment_address; use zcash_primitives::{ block::BlockHash, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; use super::{init_accounts_table, init_blocks_table, init_data_database}; - use crate::query::get_address; + use crate::{query::get_address, HRP_SAPLING_PAYMENT_ADDRESS}; #[test] fn init_accounts_table_only_works_once() { diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index df02439931..3eedaa8303 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -16,16 +16,29 @@ //! **MUST NOT** write to the database without using these APIs. Callers **MAY** read //! the database directly in order to extract information for display to users. //! +//! # Features +//! +//! The `mainnet` feature configures the light client for use with the Zcash mainnet. By +//! default, the light client is configured for use with the Zcash testnet. +//! //! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock //! [`init_cache_database`]: crate::init::init_cache_database use rusqlite::{Connection, NO_PARAMS}; use std::cmp; -use zcash_client_backend::{ - constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::encode_payment_address, -}; +use zcash_client_backend::encoding::encode_payment_address; use zcash_primitives::zip32::ExtendedFullViewingKey; +#[cfg(feature = "mainnet")] +use zcash_client_backend::constants::mainnet::{ + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +#[cfg(not(feature = "mainnet"))] +use zcash_client_backend::constants::testnet::{ + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, +}; + pub mod error; pub mod init; pub mod query; @@ -33,6 +46,11 @@ pub mod scan; pub mod transact; const ANCHOR_OFFSET: u32 = 10; + +#[cfg(feature = "mainnet")] +const SAPLING_ACTIVATION_HEIGHT: i32 = 419_200; + +#[cfg(not(feature = "mainnet"))] const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 01028d8b6d..c30abd32d9 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -5,7 +5,6 @@ use protobuf::parse_from_bytes; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; use zcash_client_backend::{ - constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock, welding_rig::scan_block, }; @@ -17,7 +16,7 @@ use zcash_primitives::{ use crate::{ error::{Error, ErrorKind}, - SAPLING_ACTIVATION_HEIGHT, + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, }; struct CompactBlockRow { diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 55b84f90fd..931e2672a9 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -5,10 +5,7 @@ use pairing::bls12_381::Bls12; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::convert::TryInto; use std::path::Path; -use zcash_client_backend::{ - constants::testnet::{HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS}, - encoding::{encode_extended_full_viewing_key, encode_payment_address}, -}; +use zcash_client_backend::encoding::{encode_extended_full_viewing_key, encode_payment_address}; use zcash_primitives::{ consensus, jubjub::fs::{Fs, FsRepr}, @@ -27,7 +24,8 @@ use zcash_primitives::{ use crate::{ error::{Error, ErrorKind}, - get_target_and_anchor_heights, + get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + HRP_SAPLING_PAYMENT_ADDRESS, }; struct SelectedNoteRow { From 380c2f726f04fe16f485d5b6d749adda3307a3fd Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 May 2019 11:27:43 +0100 Subject: [PATCH 075/210] Chain validity and reorg handling --- zcash_client_sqlite/src/chain.rs | 474 +++++++++++++++++++++++++++++++ zcash_client_sqlite/src/error.rs | 4 + zcash_client_sqlite/src/lib.rs | 1 + 3 files changed, 479 insertions(+) create mode 100644 zcash_client_sqlite/src/chain.rs diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs new file mode 100644 index 0000000000..4e94bee7cb --- /dev/null +++ b/zcash_client_sqlite/src/chain.rs @@ -0,0 +1,474 @@ +//! Functions for enforcing chain validity and handling chain reorgs. +//! +//! # Examples +//! +//! ``` +//! use zcash_client_sqlite::{ +//! chain::{rewind_to_height, validate_combined_chain}, +//! error::ErrorKind, +//! scan::scan_cached_blocks, +//! }; +//! +//! let db_cache = "/path/to/cache.db"; +//! let db_data = "/path/to/data.db"; +//! +//! // 1) Download new CompactBlocks into db_cache. +//! +//! // 2) Run the chain validator on the received blocks. +//! // +//! // Given that we assume the server always gives us correct-at-the-time blocks, any +//! // errors are in the blocks we have previously cached or scanned. +//! if let Err(e) = validate_combined_chain(&db_cache, &db_data) { +//! match e.kind() { +//! ErrorKind::InvalidChain(upper_bound, _) => { +//! // a) Pick a height to rewind to. +//! // +//! // This might be informed by some external chain reorg information, or +//! // heuristics such as the platform, available bandwidth, size of recent +//! // CompactBlocks, etc. +//! let rewind_height = upper_bound - 10; +//! +//! // b) Rewind scanned block information. +//! rewind_to_height(&db_data, rewind_height); +//! +//! // c) Delete cached blocks from rewind_height onwards. +//! // +//! // This does imply that assumed-valid blocks will be re-downloaded, but it +//! // is also possible that in the intervening time, a chain reorg has +//! // occurred that orphaned some of those blocks. +//! +//! // d) If there is some separate thread or service downloading +//! // CompactBlocks, tell it to go back and download from rewind_height +//! // onwards. +//! } +//! _ => { +//! // Handle other errors. +//! } +//! } +//! } +//! +//! // 3) Scan (any remaining) cached blocks. +//! // +//! // At this point, the cache and scanned data are locally consistent (though not +//! // necessarily consistent with the latest chain tip - this would be discovered the +//! // next time this codepath is executed after new blocks are received). +//! scan_cached_blocks(&db_cache, &db_data); +//! ``` + +use protobuf::parse_from_bytes; +use rusqlite::{Connection, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::proto::compact_formats::CompactBlock; + +use crate::{ + error::{Error, ErrorKind}, + SAPLING_ACTIVATION_HEIGHT, +}; + +#[derive(Debug)] +pub enum ChainInvalidCause { + PrevHashMismatch, + /// (expected_height, actual_height) + HeightMismatch(i32, i32), +} + +struct CompactBlockRow { + height: i32, + data: Vec, +} + +/// Checks that the scanned blocks in the data database, when combined with the recent +/// `CompactBlock`s in the cache database, form a valid chain. +/// +/// This function is built on the core assumption that the information provided in the +/// cache database is more likely to be accurate than the previously-scanned information. +/// This follows from the design (and trust) assumption that the `lightwalletd` server +/// provides accurate block information as of the time it was requested. +/// +/// Returns: +/// - `Ok(())` if the combined chain is valid. +/// - `Err(ErrorKind::InvalidChain(upper_bound, cause))` if the combined chain is invalid. +/// `upper_bound` is the height of the highest invalid block (on the assumption that the +/// highest block in the cache database is correct). +/// - `Err(e)` if there was an error during validation unrelated to chain validity. +/// +/// This function does not mutate either of the databases. +pub fn validate_combined_chain, Q: AsRef>( + db_cache: P, + db_data: Q, +) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use Sapling activation height to select all cached CompactBlocks. + let (have_scanned, last_scanned_height) = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0) + .map(|h| (true, h)) + .or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1))) + })?; + + // Fetch the CompactBlocks we need to validate + let mut stmt_blocks = cache + .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height DESC")?; + let mut rows = stmt_blocks.query_map(&[last_scanned_height], |row| { + Ok(CompactBlockRow { + height: row.get(0)?, + data: row.get(1)?, + }) + })?; + + // Take the highest cached block as accurate. + let (mut last_height, mut last_prev_hash) = { + let assumed_correct = match rows.next() { + Some(row) => row?, + None => { + // No cached blocks, and we've already validated the blocks we've scanned, + // so there's nothing to validate. + // TODO: Maybe we still want to check if there are cached blocks that are + // at heights we previously scanned? Check scanning flow again. + return Ok(()); + } + }; + let block: CompactBlock = parse_from_bytes(&assumed_correct.data)?; + (block.height as i32, block.prev_hash()) + }; + + for row in rows { + let row = row?; + + // Scanned blocks MUST be height-sequential. + if row.height != (last_height - 1) { + return Err(Error(ErrorKind::InvalidChain( + last_height - 1, + ChainInvalidCause::HeightMismatch(last_height - 1, row.height), + ))); + } + last_height = row.height; + + let block: CompactBlock = parse_from_bytes(&row.data)?; + + // Cached blocks MUST be hash-chained. + if block.hash() != last_prev_hash { + return Err(Error(ErrorKind::InvalidChain( + last_height, + ChainInvalidCause::PrevHashMismatch, + ))); + } + last_prev_hash = block.prev_hash(); + } + + if have_scanned { + // Cached blocks MUST hash-chain to the last scanned block. + let last_scanned_hash = data.query_row( + "SELECT hash FROM blocks WHERE height = ?", + &[last_scanned_height], + |row| row.get::<_, Vec<_>>(0), + )?; + if &last_scanned_hash[..] != &last_prev_hash.0[..] { + return Err(Error(ErrorKind::InvalidChain( + last_scanned_height, + ChainInvalidCause::PrevHashMismatch, + ))); + } + } + + // All good! + Ok(()) +} + +/// Rewinds the data database to the given height. +/// +/// If the requested height is greater than or equal to the height of the last scanned +/// block, this function does nothing. +pub fn rewind_to_height>(db_data: P, height: i32) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use Sapling activation height. + let last_scanned_height = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + + if height >= last_scanned_height { + // Nothing to do. + return Ok(()); + } + + // Start an SQL transaction for rewinding. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Decrement witnesses. + data.execute("DELETE FROM sapling_witnesses WHERE block > ?", &[height])?; + + // Un-mine transactions. + data.execute( + "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", + &[height], + )?; + + // Now that they aren't depended on, delete scanned blocks. + data.execute("DELETE FROM blocks WHERE height > ?", &[height])?; + + // Commit the SQL transaction, rewinding atomically. + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{rewind_to_height, validate_combined_chain}; + use crate::{ + error::ErrorKind, + init::{init_accounts_table, init_cache_database, init_data_database}, + query::get_balance, + scan::scan_cached_blocks, + tests::{fake_compact_block, insert_into_cache}, + SAPLING_ACTIVATION_HEIGHT, + }; + + #[test] + fn valid_chain_states() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Empty chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create a fake CompactBlock sending value to the address + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + insert_into_cache(db_cache, &cb); + + // Cache-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create a second fake CompactBlock sending more value to the address + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk, + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb2); + + // Data+cache chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + } + + #[test] + fn invalid_chain_cache_disconnected() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create some fake CompactBlocks + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create more fake CompactBlocks that don't connect to the scanned ones + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + BlockHash([1; 32]), + extfvk.clone(), + Amount::from_u64(8).unwrap(), + ); + let (cb4, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 3, + cb3.hash(), + extfvk.clone(), + Amount::from_u64(3).unwrap(), + ); + insert_into_cache(db_cache, &cb3); + insert_into_cache(db_cache, &cb4); + + // Data+cache chain should be invalid at the data/cache boundary + match validate_combined_chain(db_cache, db_data) { + Err(e) => match e.kind() { + ErrorKind::InvalidChain(upper_bound, _) => { + assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 1) + } + _ => panic!(), + }, + _ => panic!(), + } + } + + #[test] + fn invalid_chain_cache_reorg() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create some fake CompactBlocks + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create more fake CompactBlocks that contain a reorg + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + cb2.hash(), + extfvk.clone(), + Amount::from_u64(8).unwrap(), + ); + let (cb4, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 3, + BlockHash([1; 32]), + extfvk.clone(), + Amount::from_u64(3).unwrap(), + ); + insert_into_cache(db_cache, &cb3); + insert_into_cache(db_cache, &cb4); + + // Data+cache chain should be invalid inside the cache + match validate_combined_chain(db_cache, db_data) { + Err(e) => match e.kind() { + ErrorKind::InvalidChain(upper_bound, _) => { + assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 2) + } + _ => panic!(), + }, + _ => panic!(), + } + } + + #[test] + fn data_db_rewinding() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create fake CompactBlocks sending value to the address + let value = Amount::from_u64(5).unwrap(); + let value2 = Amount::from_u64(7).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data).unwrap(); + + // Account balance should reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + + // "Rewind" to height of last scanned block + rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT + 1).unwrap(); + + // Account balance should be unaltered + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + + // Rewind so that one block is dropped + rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT).unwrap(); + + // Account balance should only contain the first received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + } +} diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index 52ff230a17..b0dfb475ac 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -10,6 +10,7 @@ pub enum ErrorKind { CorruptedData(&'static str), IncorrectHRPExtFVK, InsufficientBalance(u64, u64), + InvalidChain(i32, crate::chain::ChainInvalidCause), InvalidExtSK(u32), InvalidHeight(i32, i32), InvalidMemo(std::str::Utf8Error), @@ -38,6 +39,9 @@ impl fmt::Display for Error { "Insufficient balance (have {}, need {} including fee)", have, need ), + ErrorKind::InvalidChain(upper_bound, cause) => { + write!(f, "Invalid chain (upper bound: {}): {:?}", upper_bound, cause) + } ErrorKind::InvalidExtSK(account) => { write!(f, "Incorrect ExtendedSpendingKey for account {}", account) } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 3eedaa8303..58fc2b1bea 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -39,6 +39,7 @@ use zcash_client_backend::constants::testnet::{ HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, }; +pub mod chain; pub mod error; pub mod init; pub mod query; From 4cfdacedecfbb9485c4550c35c2af146c1b1c83f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 May 2019 15:59:18 +0100 Subject: [PATCH 076/210] zcash_client_sqlite: Support sending to t-addrs --- zcash_client_sqlite/Cargo.toml | 1 + zcash_client_sqlite/src/address.rs | 63 +++++++++++++++++++++++++++++ zcash_client_sqlite/src/error.rs | 8 ++++ zcash_client_sqlite/src/lib.rs | 1 + zcash_client_sqlite/src/transact.rs | 30 ++++++++------ 5 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 zcash_client_sqlite/src/address.rs diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 92df0fad88..0907e42028 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] bech32 = "0.7" +bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } pairing = { version = "0.16", path = "../pairing" } protobuf = "2" diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs new file mode 100644 index 0000000000..f72512fa82 --- /dev/null +++ b/zcash_client_sqlite/src/address.rs @@ -0,0 +1,63 @@ +//! Structs for handling supported address types. + +use pairing::bls12_381::Bls12; +use zcash_client_backend::encoding::{ + decode_payment_address, decode_transparent_address, encode_payment_address, + encode_transparent_address, +}; +use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress}; + +#[cfg(feature = "mainnet")] +use zcash_client_backend::constants::mainnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +#[cfg(not(feature = "mainnet"))] +use zcash_client_backend::constants::testnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +/// An address that funds can be sent to. +pub enum RecipientAddress { + Shielded(PaymentAddress), + Transparent(TransparentAddress), +} + +impl From> for RecipientAddress { + fn from(addr: PaymentAddress) -> Self { + RecipientAddress::Shielded(addr) + } +} + +impl From for RecipientAddress { + fn from(addr: TransparentAddress) -> Self { + RecipientAddress::Transparent(addr) + } +} + +impl RecipientAddress { + pub fn from_str(s: &str) -> Option { + if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { + Some(pa.into()) + } else if let Ok(Some(addr)) = + decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s) + { + Some(addr.into()) + } else { + None + } + } + + pub fn to_string(&self) -> String { + match self { + RecipientAddress::Shielded(pa) => { + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa) + } + RecipientAddress::Transparent(addr) => encode_transparent_address( + &B58_PUBKEY_ADDRESS_PREFIX, + &B58_SCRIPT_ADDRESS_PREFIX, + addr, + ), + } + } +} diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index b0dfb475ac..ed33922b7b 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -20,6 +20,7 @@ pub enum ErrorKind { ScanRequired, TableNotEmpty, Bech32(bech32::Error), + Base58(bs58::decode::Error), Builder(builder::Error), Database(rusqlite::Error), Io(std::io::Error), @@ -65,6 +66,7 @@ impl fmt::Display for Error { ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), ErrorKind::Bech32(e) => write!(f, "{}", e), + ErrorKind::Base58(e) => write!(f, "{}", e), ErrorKind::Builder(e) => write!(f, "{:?}", e), ErrorKind::Database(e) => write!(f, "{}", e), ErrorKind::Io(e) => write!(f, "{}", e), @@ -93,6 +95,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: bs58::decode::Error) -> Self { + Error(ErrorKind::Base58(e)) + } +} + impl From for Error { fn from(e: builder::Error) -> Self { Error(ErrorKind::Builder(e)) diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 58fc2b1bea..e8f16de811 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -39,6 +39,7 @@ use zcash_client_backend::constants::testnet::{ HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, }; +pub mod address; pub mod chain; pub mod error; pub mod init; diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 931e2672a9..155059bfab 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -5,13 +5,13 @@ use pairing::bls12_381::Bls12; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::convert::TryInto; use std::path::Path; -use zcash_client_backend::encoding::{encode_extended_full_viewing_key, encode_payment_address}; +use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ consensus, jubjub::fs::{Fs, FsRepr}, merkle_tree::{IncrementalWitness, MerklePath}, note_encryption::Memo, - primitives::{Diversifier, Note, PaymentAddress}, + primitives::{Diversifier, Note}, prover::TxProver, sapling::Node, transaction::{ @@ -23,9 +23,9 @@ use zcash_primitives::{ }; use crate::{ + address::RecipientAddress, error::{Error, ErrorKind}, get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - HRP_SAPLING_PAYMENT_ADDRESS, }; struct SelectedNoteRow { @@ -63,7 +63,7 @@ struct SelectedNoteRow { /// /// let account = 0; /// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); -/// let to = extsk.default_address().unwrap().1; +/// let to = extsk.default_address().unwrap().1.into(); /// match create_to_address( /// "/path/to/data.db", /// consensus::BranchId::Sapling, @@ -82,7 +82,7 @@ pub fn create_to_address>( consensus_branch_id: consensus::BranchId, prover: impl TxProver, (account, extsk): (u32, &ExtendedSpendingKey), - to: &PaymentAddress, + to: &RecipientAddress, value: Amount, memo: Option, ) -> Result { @@ -228,7 +228,12 @@ pub fn create_to_address>( selected.merkle_path, )?; } - builder.add_sapling_output(ovk, to.clone(), value, memo.clone())?; + match to { + RecipientAddress::Shielded(to) => { + builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) + } + RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), + }?; let (tx, tx_metadata) = builder.build(consensus_branch_id, &prover)?; // We only called add_sapling_output() once. let output_index = match tx_metadata.output_index(0) { @@ -270,7 +275,8 @@ pub fn create_to_address>( } // Save the sent note in the database. - let to_str = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, to); + // TODO: Decide how to save transparent output information. + let to_str = to.to_string(); if let Some(memo) = memo { let mut stmt_insert_sent_note = data.prepare( "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) @@ -348,7 +354,7 @@ mod tests { ExtendedFullViewingKey::from(&extsk1), ]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk0.default_address().unwrap().1; + let to = extsk0.default_address().unwrap().1.into(); // Invalid extsk for the given account should cause an error match create_to_address( @@ -387,7 +393,7 @@ mod tests { let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1; + let to = extsk.default_address().unwrap().1.into(); // We cannot do anything if we aren't synchronised match create_to_address( @@ -415,7 +421,7 @@ mod tests { let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1; + let to = extsk.default_address().unwrap().1.into(); // Account balance should be zero assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); @@ -484,7 +490,7 @@ mod tests { // Spend fails because there are insufficient verified notes let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1; + let to = extsk2.default_address().unwrap().1.into(); match create_to_address( db_data, consensus::BranchId::Blossom, @@ -583,7 +589,7 @@ mod tests { // Send some of the funds to another address let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1; + let to = extsk2.default_address().unwrap().1.into(); create_to_address( db_data, consensus::BranchId::Blossom, From c8074d42b8675dd3832e30b2206b69549df8cc3c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 26 Nov 2019 14:12:34 +0000 Subject: [PATCH 077/210] zcash_client_sqlite: Store is_change as INTEGER instead of BOOLEAN Needed because SQLite internally stores BOOLEAN as INTEGER anyway, but this causes problems with newer versions of Room on Android. --- zcash_client_sqlite/src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index e6eeaffecd..cf6d3f2835 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -89,7 +89,7 @@ pub fn init_data_database>(db_data: P) -> Result<(), Error> { value INTEGER NOT NULL, rcm BLOB NOT NULL, nf BLOB NOT NULL UNIQUE, - is_change BOOLEAN NOT NULL, + is_change INTEGER NOT NULL, memo BLOB, spent INTEGER, FOREIGN KEY (tx) REFERENCES transactions(id_tx), From 9363ec36d96029a07947ae22b220ffbdf143f475 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Tue, 4 Feb 2020 11:40:50 -0500 Subject: [PATCH 078/210] Added a limit value for scanning. This provides a way to expose a more fine grained measure of scan progress. For example, by scanning in batches of 100 blocks, rather than everything that is pending. --- zcash_client_sqlite/src/chain.rs | 12 +++++------ zcash_client_sqlite/src/scan.rs | 33 +++++++++++++++++------------ zcash_client_sqlite/src/transact.rs | 14 ++++++------ 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index 4e94bee7cb..49e52c8bfd 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -52,7 +52,7 @@ //! // At this point, the cache and scanned data are locally consistent (though not //! // necessarily consistent with the latest chain tip - this would be discovered the //! // next time this codepath is executed after new blocks are received). -//! scan_cached_blocks(&db_cache, &db_data); +//! scan_cached_blocks(&db_cache, &db_data, None); //! ``` use protobuf::parse_from_bytes; @@ -268,7 +268,7 @@ mod tests { validate_combined_chain(db_cache, db_data).unwrap(); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Data-only chain should be valid validate_combined_chain(db_cache, db_data).unwrap(); @@ -286,7 +286,7 @@ mod tests { validate_combined_chain(db_cache, db_data).unwrap(); // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Data-only chain should be valid validate_combined_chain(db_cache, db_data).unwrap(); @@ -324,7 +324,7 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Data-only chain should be valid validate_combined_chain(db_cache, db_data).unwrap(); @@ -389,7 +389,7 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Data-only chain should be valid validate_combined_chain(db_cache, db_data).unwrap(); @@ -454,7 +454,7 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Account balance should reflect both received notes assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index c30abd32d9..4a087fc43f 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -30,8 +30,13 @@ struct WitnessRow { witness: IncrementalWitness, } -/// Scans new blocks added to the cache for any transactions received by the tracked -/// accounts. +/// Scans at most `limit` new blocks added to the cache for any transactions received by +/// the tracked accounts. +/// +/// This function will return without error after scanning at most `limit` new blocks, to +/// enable the caller to update their UI with scanning progress. Repeatedly calling this +/// function will process sequential ranges of blocks, and is equivalent to calling +/// `scan_cached_blocks` and passing `None` for the optional `limit` value. /// /// This function pays attention only to cached blocks with heights greater than the /// highest scanned block in `db_data`. Cached blocks with lower heights are not verified @@ -50,13 +55,14 @@ struct WitnessRow { /// ``` /// use zcash_client_sqlite::scan::scan_cached_blocks; /// -/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db"); +/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db", None); /// ``` /// /// [`init_blocks_table`]: crate::init::init_blocks_table pub fn scan_cached_blocks, Q: AsRef>( db_cache: P, db_data: Q, + limit: Option, ) -> Result<(), Error> { let cache = Connection::open(db_cache)?; let data = Connection::open(db_data)?; @@ -68,9 +74,10 @@ pub fn scan_cached_blocks, Q: AsRef>( })?; // Fetch the CompactBlocks we need to scan - let mut stmt_blocks = cache - .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC")?; - let rows = stmt_blocks.query_map(&[last_height], |row| { + let mut stmt_blocks = cache.prepare( + "SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC LIMIT ?", + )?; + let rows = stmt_blocks.query_map(&[last_height, limit.unwrap_or(i32::max_value())], |row| { Ok(CompactBlockRow { height: row.get(0)?, data: row.get(1)?, @@ -356,7 +363,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb1); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); assert_eq!(get_balance(db_data, 0).unwrap(), value); // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next @@ -373,7 +380,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb3); - match scan_cached_blocks(db_cache, db_data) { + match scan_cached_blocks(db_cache, db_data, None) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( e.to_string(), @@ -387,7 +394,7 @@ mod tests { // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both insert_into_cache(db_cache, &cb2); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); assert_eq!( get_balance(db_data, 0).unwrap(), Amount::from_u64(150_000).unwrap() @@ -423,7 +430,7 @@ mod tests { insert_into_cache(db_cache, &cb); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Account balance should reflect the received note assert_eq!(get_balance(db_data, 0).unwrap(), value); @@ -434,7 +441,7 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Account balance should reflect both received notes assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); @@ -469,7 +476,7 @@ mod tests { insert_into_cache(db_cache, &cb); // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Account balance should reflect the received note assert_eq!(get_balance(db_data, 0).unwrap(), value); @@ -491,7 +498,7 @@ mod tests { ); // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Account balance should equal the change assert_eq!(get_balance(db_data, 0).unwrap(), value - value2); diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 155059bfab..d1d9346f61 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -468,7 +468,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Verified balance matches total balance assert_eq!(get_balance(db_data, 0).unwrap(), value); @@ -482,7 +482,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Verified balance does not include the second note assert_eq!(get_balance(db_data, 0).unwrap(), value + value); @@ -518,7 +518,7 @@ mod tests { ); insert_into_cache(db_cache, &cb); } - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Second spend still fails match create_to_address( @@ -545,7 +545,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Second spend should now succeed create_to_address( @@ -584,7 +584,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); assert_eq!(get_balance(db_data, 0).unwrap(), value); // Send some of the funds to another address @@ -629,7 +629,7 @@ mod tests { ); insert_into_cache(db_cache, &cb); } - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Second spend still fails match create_to_address( @@ -656,7 +656,7 @@ mod tests { value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); + scan_cached_blocks(db_cache, db_data, None).unwrap(); // Second spend should now succeed create_to_address( From 3036064cd09e1068d1162e5c374ef83c3bb689a3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 6 Mar 2020 09:36:50 +1300 Subject: [PATCH 079/210] zcash_client_sqlite: Update received note during scan if present Fixes a bug where rewinding a block that contained a received note would cause a constraint violation. --- zcash_client_sqlite/src/chain.rs | 6 +++++ zcash_client_sqlite/src/scan.rs | 39 +++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index 49e52c8bfd..dfcaf265e0 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -470,5 +470,11 @@ mod tests { // Account balance should only contain the first received note assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should again reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); } } diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 4a087fc43f..7cc66b1536 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -143,10 +143,17 @@ pub fn scan_cached_blocks, Q: AsRef>( let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; let mut stmt_mark_spent_note = data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; + let mut stmt_update_note = data.prepare( + "UPDATE received_notes + SET account = ?, diversifier = ?, value = ?, rcm = ?, nf = ?, is_change = ? + WHERE tx = ? AND output_index = ?", + )?; let mut stmt_insert_note = data.prepare( "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", )?; + let mut stmt_select_note = + data.prepare("SELECT id_note FROM received_notes WHERE tx = ? AND output_index = ?")?; let mut stmt_insert_witness = data.prepare( "INSERT INTO sapling_witnesses (note, block, witness) VALUES (?, ?, ?)", @@ -267,21 +274,41 @@ pub fn scan_cached_blocks, Q: AsRef>( &JUBJUB, ); - // Insert received note into the database. // Assumptions: // - A transaction will not contain more than 2^63 shielded outputs. // - A note value will never exceed 2^63 zatoshis. - stmt_insert_note.execute(&[ - tx_row.to_sql()?, - (output.index as i64).to_sql()?, + + // First try updating an existing received note into the database. + let note_row = if stmt_update_note.execute(&[ (output.account as i64).to_sql()?, output.to.diversifier().0.to_sql()?, (output.note.value as i64).to_sql()?, rcm.as_ref().to_sql()?, nf.to_sql()?, output.is_change.to_sql()?, - ])?; - let note_row = data.last_insert_rowid(); + tx_row.to_sql()?, + (output.index as i64).to_sql()?, + ])? == 0 + { + // It isn't there, so insert our note into the database. + stmt_insert_note.execute(&[ + tx_row.to_sql()?, + (output.index as i64).to_sql()?, + (output.account as i64).to_sql()?, + output.to.diversifier().0.to_sql()?, + (output.note.value as i64).to_sql()?, + rcm.as_ref().to_sql()?, + nf.to_sql()?, + output.is_change.to_sql()?, + ])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_note.query_row( + &[tx_row.to_sql()?, (output.index as i64).to_sql()?], + |row| row.get(0), + )? + }; // Save witness for note. witnesses.push(WitnessRow { From b1a2e8172d5aacf7758fd9d5f675fadef4c3260f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 5 Mar 2020 14:01:10 +1300 Subject: [PATCH 080/210] zcash_client_backend: Add account to DecryptedOutput --- zcash_client_backend/src/decrypt.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index f9830f61be..df44a2c679 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -15,6 +15,8 @@ pub struct DecryptedOutput { pub index: usize, /// The note within the output. pub note: Note, + /// The account that decrypted the note. + pub account: usize, /// The address the note was sent to. pub to: PaymentAddress, /// The memo included with the note. @@ -46,7 +48,7 @@ pub fn decrypt_transaction( None => continue, }; - for (ivk, ovk) in &vks { + for (account, (ivk, ovk)) in vks.iter().enumerate() { let ((note, to, memo), outgoing) = match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) { Some(ret) => (ret, false), @@ -65,6 +67,7 @@ pub fn decrypt_transaction( decrypted.push(DecryptedOutput { index, note, + account, to, memo, outgoing, From 131e00e25d54930afa770deb8a259df4c70f3bfd Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 5 Mar 2020 14:07:01 +1300 Subject: [PATCH 081/210] zcash_client_sqlite::scan::decrypt_and_store_transaction --- zcash_client_sqlite/src/scan.rs | 144 +++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 7cc66b1536..8dae82bb59 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -5,16 +5,18 @@ use protobuf::parse_from_bytes; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; use zcash_client_backend::{ - encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock, - welding_rig::scan_block, + decrypt_transaction, encoding::decode_extended_full_viewing_key, + proto::compact_formats::CompactBlock, welding_rig::scan_block, }; use zcash_primitives::{ merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, + transaction::Transaction, JUBJUB, }; use crate::{ + address::RecipientAddress, error::{Error, ErrorKind}, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, }; @@ -349,6 +351,144 @@ pub fn scan_cached_blocks, Q: AsRef>( Ok(()) } +/// Scans a [`Transaction`] for any information that can be decrypted by the accounts in +/// the wallet, and saves it to the wallet. +pub fn decrypt_and_store_transaction>( + db_data: P, + tx: &Transaction, +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + // Fetch the ExtendedFullViewingKeys we are tracking + let mut stmt_fetch_accounts = + data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; + let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { + row.get(0).map(|extfvk: String| { + decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + }) + })?; + // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. + let extfvks: Vec<_> = extfvks + .collect::, _>, _>>()?? + .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; + + let outputs = decrypt_transaction(tx, &extfvks); + + if outputs.is_empty() { + // Nothing to see here + return Ok(()); + } + + let mut stmt_update_tx = data.prepare( + "UPDATE transactions + SET expiry_height = ?, raw = ? WHERE txid = ?", + )?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, expiry_height, raw) + VALUES (?, ?, ?)", + )?; + let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; + let mut stmt_update_sent_note = data.prepare( + "UPDATE sent_notes + SET from_account = ?, address = ?, value = ?, memo = ? + WHERE tx = ? AND output_index = ?", + )?; + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) + VALUES (?, ?, ?, ?, ?, ?)", + )?; + let mut stmt_update_received_note = data.prepare( + "UPDATE received_notes + SET account = ?, diversifier = ?, value = ?, rcm = ?, memo = ? + WHERE tx = ? AND output_index = ?", + )?; + let mut stmt_insert_received_note = data.prepare( + "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, memo) + VALUES (?, ?, ?, ?, ?, ?, ?)", + )?; + + // Update the database atomically, to ensure the result is internally consistent. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // First try update an existing transaction in the database. + let txid = tx.txid().0.to_vec(); + let mut raw_tx = vec![]; + tx.write(&mut raw_tx)?; + let tx_row = if stmt_update_tx.execute(&[ + tx.expiry_height.to_sql()?, + raw_tx.to_sql()?, + txid.to_sql()?, + ])? == 0 + { + // It isn't there, so insert our transaction into the database. + stmt_insert_tx.execute(&[txid.to_sql()?, tx.expiry_height.to_sql()?, raw_tx.to_sql()?])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_tx.query_row(&[txid], |row| row.get(0))? + }; + + for output in outputs { + let output_index = output.index as i64; + let account = output.account as i64; + let value = output.note.value as i64; + + if output.outgoing { + let to_str = RecipientAddress::from(output.to).to_string(); + + // Try updating an existing sent note. + if stmt_update_sent_note.execute(&[ + account.to_sql()?, + to_str.to_sql()?, + value.to_sql()?, + output.memo.as_bytes().to_sql()?, + tx_row.to_sql()?, + output_index.to_sql()?, + ])? == 0 + { + // It isn't there, so insert. + stmt_insert_sent_note.execute(&[ + tx_row.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + value.to_sql()?, + output.memo.as_bytes().to_sql()?, + ])?; + } + } else { + let rcm = output.note.r.to_repr(); + + // Try updating an existing received note. + if stmt_update_received_note.execute(&[ + account.to_sql()?, + output.to.diversifier().0.to_sql()?, + value.to_sql()?, + rcm.as_ref().to_sql()?, + output.memo.as_bytes().to_sql()?, + tx_row.to_sql()?, + output_index.to_sql()?, + ])? == 0 + { + // It isn't there, so insert. + stmt_insert_received_note.execute(&[ + tx_row.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + output.to.diversifier().0.to_sql()?, + value.to_sql()?, + rcm.as_ref().to_sql()?, + output.memo.as_bytes().to_sql()?, + ])?; + } + } + } + + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + #[cfg(test)] mod tests { use tempfile::NamedTempFile; From d71a5991918305c9ded9b743a98387c5f34f76d8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 12:53:07 +1200 Subject: [PATCH 082/210] Empty equihash crate --- Cargo.toml | 1 + components/equihash/Cargo.toml | 11 +++++++++++ components/equihash/src/lib.rs | 7 +++++++ 3 files changed, 19 insertions(+) create mode 100644 components/equihash/Cargo.toml create mode 100644 components/equihash/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index dd595b1bd3..a916d8c59f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "bellman", + "components/equihash", "ff", "group", "pairing", diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml new file mode 100644 index 0000000000..87caa19173 --- /dev/null +++ b/components/equihash/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "equihash" +description = "The Equihash Proof-of-Work function" +version = "0.0.0" +authors = ["Jack Grigg "] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs new file mode 100644 index 0000000000..31e1bb209f --- /dev/null +++ b/components/equihash/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From 5b2c71e112ade5488ba49bb16f50897d513702f8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 13:01:04 +1200 Subject: [PATCH 083/210] Move Equihash verification APIs into equihash crate --- components/equihash/Cargo.toml | 3 +++ components/equihash/src/lib.rs | 12 +++++------- .../equihash.rs => components/equihash/src/verify.rs | 0 zcash_primitives/Cargo.toml | 1 + zcash_primitives/src/block.rs | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) rename zcash_primitives/src/block/equihash.rs => components/equihash/src/verify.rs (100%) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index 87caa19173..edbdc4f7cd 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -9,3 +9,6 @@ license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +blake2b_simd = "0.5" +byteorder = "1" +log = "0.4" diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 31e1bb209f..087f95fb74 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -1,7 +1,5 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +//! The Equihash Proof-of-Work function. + +mod verify; + +pub use verify::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; diff --git a/zcash_primitives/src/block/equihash.rs b/components/equihash/src/verify.rs similarity index 100% rename from zcash_primitives/src/block/equihash.rs rename to components/equihash/src/verify.rs diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index b40744db01..bfd25d9e79 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -17,6 +17,7 @@ blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.2.1" +equihash = { version = "0.0", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" hex = "0.3" diff --git a/zcash_primitives/src/block.rs b/zcash_primitives/src/block.rs index 8432cd4c93..fd1edbc032 100644 --- a/zcash_primitives/src/block.rs +++ b/zcash_primitives/src/block.rs @@ -9,7 +9,7 @@ use std::ops::Deref; use crate::serialize::Vector; -pub mod equihash; +pub use equihash; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHash(pub [u8; 32]); From 0a61db0317f2f1ac1b9d675638df1dc74fe3f9df Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 21:06:42 +1200 Subject: [PATCH 084/210] Upgrade to rusqlite 0.23 Requires bumping the MSRV to 1.40.0 because libsqlite3-sys uses features introduced in that version. remove_dir_all can similarly be unpinned. --- .github/workflows/ci.yml | 4 ++-- .travis.yml | 2 +- rust-toolchain | 2 +- zcash_client_sqlite/Cargo.toml | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5af4ce3a50..7724e4ab2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.39.0 + toolchain: 1.40.0 override: true # cargo fmt does not build the code, and running it in a fresh clone of @@ -48,7 +48,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.39.0 + toolchain: 1.40.0 override: true - name: cargo fetch uses: actions-rs/cargo@v1 diff --git a/.travis.yml b/.travis.yml index c72494c781..e77c9e2961 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.39.0 + - 1.40.0 cache: cargo diff --git a/rust-toolchain b/rust-toolchain index 5edffce6d5..32b7211cb6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.39.0 +1.40.0 diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 0907e42028..e1fe38b22e 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -17,14 +17,13 @@ bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } pairing = { version = "0.16", path = "../pairing" } protobuf = "2" -rusqlite = { version = "0.20", features = ["bundled"] } +rusqlite = { version = "0.23", features = ["bundled"] } time = "0.1" zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [dev-dependencies] rand_core = "0.5.1" -remove_dir_all = "=0.5.2" # tempfile dependency; 0.5.3 bumped the MSRV tempfile = "3" zcash_proofs = { version = "0.2", path = "../zcash_proofs" } From e910788e8e6c2b8e0002fbb843441a35510aa5c9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 01:31:08 +1200 Subject: [PATCH 085/210] zcash_proofs: Extract default params folder logic --- zcash_proofs/src/lib.rs | 21 +++++++++++++++++++++ zcash_proofs/src/prover.rs | 21 ++++++++------------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 7faca6c35c..7c579bf245 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -12,6 +12,11 @@ use std::fs::File; use std::io::{self, BufReader}; use std::path::Path; +#[cfg(feature = "directories")] +use directories::BaseDirs; +#[cfg(feature = "directories")] +use std::path::PathBuf; + pub mod circuit; mod hashreader; pub mod sapling; @@ -20,6 +25,22 @@ pub mod sprout; #[cfg(feature = "local-prover")] pub mod prover; +// Circuit names +const SAPLING_SPEND_NAME: &str = "sapling-spend.params"; +const SAPLING_OUTPUT_NAME: &str = "sapling-output.params"; + +/// Returns the default folder that the Zcash proving parameters are located in. +#[cfg(feature = "directories")] +fn default_params_folder() -> Option { + BaseDirs::new().map(|base_dirs| { + if cfg!(any(windows, target_os = "macos")) { + base_dirs.data_dir().join("ZcashParams") + } else { + base_dirs.home_dir().join(".zcash-params") + } + }) +} + pub fn load_parameters( spend_path: &Path, output_path: &Path, diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index 3dd665a7da..d1c23aac7a 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -1,7 +1,6 @@ //! Abstractions over the proving system and parameters for ease of use. use bellman::groth16::{Parameters, PreparedVerifyingKey}; -use directories::BaseDirs; use pairing::bls12_381::{Bls12, Fr}; use std::path::Path; use zcash_primitives::{ @@ -17,7 +16,10 @@ use zcash_primitives::{ JUBJUB, }; -use crate::{load_parameters, sapling::SaplingProvingContext}; +use crate::{ + default_params_folder, load_parameters, sapling::SaplingProvingContext, SAPLING_OUTPUT_NAME, + SAPLING_SPEND_NAME, +}; /// An implementation of [`TxProver`] using Sapling Spend and Output parameters from /// locally-accessible paths. @@ -78,18 +80,11 @@ impl LocalTxProver { /// This function will panic if the parameters in the default local location do not /// have the expected hashes. pub fn with_default_location() -> Option { - let base_dirs = BaseDirs::new()?; - let unix_params_dir = base_dirs.home_dir().join(".zcash-params"); - let win_osx_params_dir = base_dirs.data_dir().join("ZcashParams"); - let (spend_path, output_path) = if unix_params_dir.exists() { - ( - unix_params_dir.join("sapling-spend.params"), - unix_params_dir.join("sapling-output.params"), - ) - } else if win_osx_params_dir.exists() { + let params_dir = default_params_folder()?; + let (spend_path, output_path) = if params_dir.exists() { ( - win_osx_params_dir.join("sapling-spend.params"), - win_osx_params_dir.join("sapling-output.params"), + params_dir.join(SAPLING_SPEND_NAME), + params_dir.join(SAPLING_OUTPUT_NAME), ) } else { return None; From 90126721641a9a398fcb683d53e52db9f5c60e9e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 12:38:40 +1200 Subject: [PATCH 086/210] zcash_proofs: Add API for downloading the Sapling parameters Includes an example that exposes the API as a binary. --- zcash_proofs/Cargo.toml | 6 +++ zcash_proofs/examples/download-params.rs | 3 ++ zcash_proofs/src/lib.rs | 65 ++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 zcash_proofs/examples/download-params.rs diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 3d2a8d4bec..62d568cef8 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -17,6 +17,7 @@ blake2b_simd = "0.5" byteorder = "1" directories = { version = "1", optional = true } ff = { version = "0.6", path = "../ff" } +minreq = { version = "2", features = ["https"], optional = true } pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } @@ -26,8 +27,13 @@ rand_xorshift = "0.2" [features] default = ["local-prover", "multicore"] +download-params = ["minreq"] local-prover = ["directories"] multicore = ["bellman/multicore"] +[[example]] +name = "download-params" +required-features = ["download-params"] + [badges] maintenance = { status = "actively-developed" } diff --git a/zcash_proofs/examples/download-params.rs b/zcash_proofs/examples/download-params.rs new file mode 100644 index 0000000000..b23cb75fae --- /dev/null +++ b/zcash_proofs/examples/download-params.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), minreq::Error> { + zcash_proofs::download_parameters() +} diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 7c579bf245..e952be6bee 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -29,6 +29,14 @@ pub mod prover; const SAPLING_SPEND_NAME: &str = "sapling-spend.params"; const SAPLING_OUTPUT_NAME: &str = "sapling-output.params"; +// Circuit hashes +const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; +const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; +const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; + +#[cfg(feature = "download-params")] +const DOWNLOAD_URL: &str = "https://download.z.cash/downloads"; + /// Returns the default folder that the Zcash proving parameters are located in. #[cfg(feature = "directories")] fn default_params_folder() -> Option { @@ -41,6 +49,58 @@ fn default_params_folder() -> Option { }) } +/// Download the Zcash Sapling parameters, storing them in the default location. +/// +/// This mirrors the behaviour of the `fetch-params.sh` script from `zcashd`. +#[cfg(feature = "download-params")] +pub fn download_parameters() -> Result<(), minreq::Error> { + // Ensure that the default Zcash parameters location exists. + let params_dir = default_params_folder().ok_or(io::Error::new( + io::ErrorKind::Other, + "Could not load default params folder", + ))?; + std::fs::create_dir_all(¶ms_dir)?; + + let fetch_params = |name: &str, expected_hash: &str| -> Result<(), minreq::Error> { + use std::io::Write; + + // Download the parts directly (Sapling parameters are small enough for this). + let part_1 = minreq::get(format!("{}/{}.part.1", DOWNLOAD_URL, name)).send()?; + let part_2 = minreq::get(format!("{}/{}.part.2", DOWNLOAD_URL, name)).send()?; + + // Verify parameter file hash. + let hash = blake2b_simd::State::new() + .update(part_1.as_bytes()) + .update(part_2.as_bytes()) + .finalize() + .to_hex(); + if &hash != expected_hash { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!( + "{} failed validation (expected: {}, actual: {}, fetched {} bytes)", + name, + expected_hash, + hash, + part_1.as_bytes().len() + part_2.as_bytes().len() + ), + ) + .into()); + } + + // Write parameter file. + let mut f = File::create(params_dir.join(name))?; + f.write_all(part_1.as_bytes())?; + f.write_all(part_2.as_bytes())?; + Ok(()) + }; + + fetch_params(SAPLING_SPEND_NAME, SAPLING_SPEND_HASH)?; + fetch_params(SAPLING_OUTPUT_NAME, SAPLING_OUTPUT_HASH)?; + + Ok(()) +} + pub fn load_parameters( spend_path: &Path, output_path: &Path, @@ -52,11 +112,6 @@ pub fn load_parameters( PreparedVerifyingKey, Option>, ) { - // Sapling circuit hashes - const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; - const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; - const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; - // Load from each of the paths let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file"); let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file"); From bbc3ec54c785225068477187d16ac145f74c789c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 12:48:44 +1200 Subject: [PATCH 087/210] CI: Fetch and cache Sapling parameters for tests --- .github/workflows/ci.yml | 15 +++++++++++++++ .travis.yml | 19 ------------------- zcash_proofs/Cargo.toml | 4 ++++ zcash_proofs/examples/get-params-path.rs | 7 +++++++ zcash_proofs/src/lib.rs | 2 +- 5 files changed, 27 insertions(+), 20 deletions(-) delete mode 100644 .travis.yml create mode 100644 zcash_proofs/examples/get-params-path.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7724e4ab2b..3735bf199c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,21 @@ jobs: with: toolchain: 1.40.0 override: true + + - name: Fetch path to Zcash parameters + working-directory: ./zcash_proofs + run: echo "::set-env name=ZCASH_PARAMS::$(cargo run --release --example get-params-path --features directories)" + - name: Cache Zcash parameters + id: cache-params + uses: actions/cache@v2 + with: + path: ${{ env.ZCASH_PARAMS }} + key: ${{ runner.os }}-params + - name: Fetch Zcash parameters + if: steps.cache-params.outputs.cache-hit != 'true' + working-directory: ./zcash_proofs + run: cargo run --release --example download-params --features download-params + - name: cargo fetch uses: actions-rs/cargo@v1 with: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e77c9e2961..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: rust -rust: - - 1.40.0 - -cache: cargo - -before_script: - - rustup component add rustfmt - -script: - - cargo build --verbose --release --all - - cargo fmt --all -- --check - - cargo test --verbose --release --all - - cargo test --verbose --release --all -- --ignored - -before_cache: - - rm -rf "$TRAVIS_HOME/.cargo/registry/src" - - cargo install cargo-update || echo "cargo-update already installed" - - cargo install-update -a # update outdated cached binaries diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 62d568cef8..4a58e9dfbe 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -31,6 +31,10 @@ download-params = ["minreq"] local-prover = ["directories"] multicore = ["bellman/multicore"] +[[example]] +name = "get-params-path" +required-features = ["directories"] + [[example]] name = "download-params" required-features = ["download-params"] diff --git a/zcash_proofs/examples/get-params-path.rs b/zcash_proofs/examples/get-params-path.rs new file mode 100644 index 0000000000..224b2f5ead --- /dev/null +++ b/zcash_proofs/examples/get-params-path.rs @@ -0,0 +1,7 @@ +fn main() { + if let Some(path) = zcash_proofs::default_params_folder() { + if let Some(path) = path.to_str() { + println!("{}", path); + } + } +} diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index e952be6bee..e14a269530 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -39,7 +39,7 @@ const DOWNLOAD_URL: &str = "https://download.z.cash/downloads"; /// Returns the default folder that the Zcash proving parameters are located in. #[cfg(feature = "directories")] -fn default_params_folder() -> Option { +pub fn default_params_folder() -> Option { BaseDirs::new().map(|base_dirs| { if cfg!(any(windows, target_os = "macos")) { base_dirs.data_dir().join("ZcashParams") From 997657f2562e9d677b12b5be65fab8103c2f7181 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Tue, 7 Jul 2020 02:00:53 -0700 Subject: [PATCH 088/210] Move `error!` logs into proper error type (#14) hey kid, want some error handling? --- components/equihash/src/lib.rs | 4 +- components/equihash/src/verify.rs | 141 +++++++++++++++++++----------- 2 files changed, 91 insertions(+), 54 deletions(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 087f95fb74..54b1923811 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -2,4 +2,6 @@ mod verify; -pub use verify::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; +pub use verify::{ + is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive, Error, +}; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 38518baa05..3be351aa18 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -4,7 +4,7 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; -use log::error; +use std::fmt; use std::io::Cursor; use std::mem::size_of; @@ -98,6 +98,36 @@ impl Node { } } +#[derive(Debug)] +pub struct Error(Kind); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Invalid solution: {}", self.0) + } +} + +impl std::error::Error for Error {} + +#[derive(Debug)] +enum Kind { + Collision, + OutOfOrder, + DuplicateIdxs, + NonZeroRootHash, +} + +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::Collision => f.write_str("invalid collision length between StepRows"), + Kind::OutOfOrder => f.write_str("Index tree incorrectly ordered"), + Kind::DuplicateIdxs => f.write_str("duplicate indices"), + Kind::NonZeroRootHash => f.write_str("root hash of tree is non-zero"), + } + } +} + fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { let mut personalization: Vec = Vec::from("ZcashPoW"); personalization.write_u32::(n).unwrap(); @@ -199,18 +229,15 @@ fn distinct_indices(a: &Node, b: &Node) -> bool { true } -fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { +fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { if !has_collision(a, b, p.collision_byte_length()) { - error!("Invalid solution: invalid collision length between StepRows"); - false + Err(Kind::Collision) } else if b.indices_before(a) { - error!("Invalid solution: Index tree incorrectly ordered"); - false + Err(Kind::OutOfOrder) } else if !distinct_indices(a, b) { - error!("Invalid solution: duplicate indices"); - false + Err(Kind::DuplicateIdxs) } else { - true + Ok(()) } } @@ -220,7 +247,7 @@ pub fn is_valid_solution_iterative( input: &[u8], nonce: &[u8], indices: &[u32], -) -> bool { +) -> Result<(), Error> { let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); @@ -238,9 +265,7 @@ pub fn is_valid_solution_iterative( for pair in rows.chunks(2) { let a = &pair[0]; let b = &pair[1]; - if !validate_subtrees(&p, a, b) { - return false; - } + validate_subtrees(&p, a, b).map_err(Error)?; cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); } rows = cur_rows; @@ -248,28 +273,24 @@ pub fn is_valid_solution_iterative( } assert!(rows.len() == 1); - rows[0].is_zero(hash_len) + + if rows[0].is_zero(hash_len) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) + } } -fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option { +fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result { if indices.len() > 1 { let end = indices.len(); let mid = end / 2; - match ( - tree_validator(p, state, &indices[0..mid]), - tree_validator(p, state, &indices[mid..end]), - ) { - (Some(a), Some(b)) => { - if validate_subtrees(p, &a, &b) { - Some(Node::from_children(a, b, p.collision_byte_length())) - } else { - None - } - } - _ => None, - } + let a = tree_validator(p, state, &indices[0..mid])?; + let b = tree_validator(p, state, &indices[mid..end])?; + validate_subtrees(p, &a, &b).map_err(Error)?; + Ok(Node::from_children(a, b, p.collision_byte_length())) } else { - Some(Node::new(&p, &state, indices[0])) + Ok(Node::new(&p, &state, indices[0])) } } @@ -279,23 +300,30 @@ pub fn is_valid_solution_recursive( input: &[u8], nonce: &[u8], indices: &[u32], -) -> bool { +) -> Result<(), Error> { let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); - match tree_validator(&p, &state, indices) { - Some(root) => { - // Hashes were trimmed, so only need to check remaining length - root.is_zero(p.collision_byte_length()) - } - None => false, + let root = tree_validator(&p, &state, indices)?; + + // Hashes were trimmed, so only need to check remaining length + if root.is_zero(p.collision_byte_length()) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) } } -pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { +pub fn is_valid_solution( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + soln: &[u8], +) -> Result<(), Error> { let p = Params { n, k }; let indices = indices_from_minimal(soln, p.collision_bit_length()); @@ -307,12 +335,19 @@ pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8] mod tests { use super::is_valid_solution_iterative; use super::is_valid_solution_recursive; - - fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool { - let a = is_valid_solution_iterative(n, k, input, nonce, indices); - let b = is_valid_solution_recursive(n, k, input, nonce, indices); - assert!(a == b); - a + use super::Error; + + fn is_valid_solution( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + indices: &[u32], + ) -> Result<(), Error> { + is_valid_solution_iterative(n, k, input, nonce, indices)?; + is_valid_solution_recursive(n, k, input, nonce, indices)?; + + Ok(()) } #[test] @@ -324,14 +359,14 @@ mod tests { 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, @@ -387,19 +422,19 @@ mod tests { 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, 1971986, ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); nonce[0] = 1; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(!is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap_err(); indices = vec![ 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); indices = vec![ 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, @@ -455,8 +490,8 @@ mod tests { 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, 1822645, 1082368, 1392894, ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); + is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; indices = vec![ @@ -464,6 +499,6 @@ mod tests { 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, ]; - assert!(is_valid_solution(96, 5, input2, &nonce, &indices)); + is_valid_solution(96, 5, input2, &nonce, &indices).unwrap(); } } From 8759684fad248f5f8d42b8f97685ac42a42c79af Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 7 Jul 2020 22:09:24 +1200 Subject: [PATCH 089/210] equihash: Add parameter validity checks --- components/equihash/src/verify.rs | 44 +++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 3be351aa18..f32f7e2569 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -8,7 +8,8 @@ use std::fmt; use std::io::Cursor; use std::mem::size_of; -struct Params { +#[derive(Clone, Copy)] +pub struct Params { n: u32, k: u32, } @@ -20,6 +21,13 @@ struct Node { } impl Params { + pub fn new(n: u32, k: u32) -> Result { + if (k < n) && (n % 8 == 0) { + Ok(Params { n, k }) + } else { + Err(Error(Kind::InvalidParams)) + } + } fn indices_per_hash_output(&self) -> u32 { 512 / self.n } @@ -111,6 +119,7 @@ impl std::error::Error for Error {} #[derive(Debug)] enum Kind { + InvalidParams, Collision, OutOfOrder, DuplicateIdxs, @@ -120,6 +129,7 @@ enum Kind { impl fmt::Display for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Kind::InvalidParams => f.write_str("invalid parameters"), Kind::Collision => f.write_str("invalid collision length between StepRows"), Kind::OutOfOrder => f.write_str("Index tree incorrectly ordered"), Kind::DuplicateIdxs => f.write_str("duplicate indices"), @@ -193,7 +203,12 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { vout } -fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { +fn indices_from_minimal(p: Params, minimal: &[u8]) -> Result, Error> { + let c_bit_len = p.collision_bit_length(); + if minimal.len() != (1 << p.k) * (c_bit_len + 1) / 8 { + return Err(Error(Kind::InvalidParams)); + } + assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; @@ -207,7 +222,7 @@ fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { ret.push(i); } - ret + Ok(ret) } fn has_collision(a: &Node, b: &Node, len: usize) -> bool { @@ -242,14 +257,11 @@ fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { } pub fn is_valid_solution_iterative( - n: u32, - k: u32, + p: Params, input: &[u8], nonce: &[u8], indices: &[u32], ) -> Result<(), Error> { - let p = Params { n, k }; - let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); @@ -295,14 +307,11 @@ fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result Result<(), Error> { - let p = Params { n, k }; - let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); @@ -324,18 +333,18 @@ pub fn is_valid_solution( nonce: &[u8], soln: &[u8], ) -> Result<(), Error> { - let p = Params { n, k }; - let indices = indices_from_minimal(soln, p.collision_bit_length()); + let p = Params::new(n, k)?; + let indices = indices_from_minimal(p, soln)?; // Recursive validation is faster - is_valid_solution_recursive(n, k, input, nonce, &indices) + is_valid_solution_recursive(p, input, nonce, &indices) } #[cfg(test)] mod tests { use super::is_valid_solution_iterative; use super::is_valid_solution_recursive; - use super::Error; + use super::{Error, Params}; fn is_valid_solution( n: u32, @@ -344,8 +353,9 @@ mod tests { nonce: &[u8], indices: &[u32], ) -> Result<(), Error> { - is_valid_solution_iterative(n, k, input, nonce, indices)?; - is_valid_solution_recursive(n, k, input, nonce, indices)?; + let p = Params::new(n, k).unwrap(); + is_valid_solution_iterative(p, input, nonce, indices)?; + is_valid_solution_recursive(p, input, nonce, indices)?; Ok(()) } From bcd687cffbd8231856e6d98b624ee544e02c9e18 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 8 Jul 2020 15:13:41 +1200 Subject: [PATCH 090/210] equihash: Remove iterative and recursive APIs The canonical verification API from Heartwood activation is the recursive API exposed through is_valid_solution. We retain is_valid_solution_iterative internally for testing. --- components/equihash/src/lib.rs | 4 +--- components/equihash/src/verify.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index 54b1923811..e45a7d999d 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -2,6 +2,4 @@ mod verify; -pub use verify::{ - is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive, Error, -}; +pub use verify::{is_valid_solution, Error}; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index f32f7e2569..e2ee033385 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -9,7 +9,7 @@ use std::io::Cursor; use std::mem::size_of; #[derive(Clone, Copy)] -pub struct Params { +struct Params { n: u32, k: u32, } @@ -21,7 +21,7 @@ struct Node { } impl Params { - pub fn new(n: u32, k: u32) -> Result { + fn new(n: u32, k: u32) -> Result { if (k < n) && (n % 8 == 0) { Ok(Params { n, k }) } else { @@ -40,6 +40,7 @@ impl Params { fn collision_byte_length(&self) -> usize { (self.collision_bit_length() + 7) / 8 } + #[cfg(test)] fn hash_length(&self) -> usize { ((self.k as usize) + 1) * self.collision_byte_length() } @@ -76,6 +77,7 @@ impl Node { Node { hash, indices } } + #[cfg(test)] fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { let hash: Vec<_> = a .hash @@ -256,7 +258,8 @@ fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { } } -pub fn is_valid_solution_iterative( +#[cfg(test)] +fn is_valid_solution_iterative( p: Params, input: &[u8], nonce: &[u8], @@ -306,7 +309,7 @@ fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result Date: Wed, 8 Jul 2020 17:34:52 +1200 Subject: [PATCH 091/210] equihash: Crate documentation --- components/equihash/src/lib.rs | 19 ++++++++++++++++++- components/equihash/src/verify.rs | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index e45a7d999d..e297308094 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -1,4 +1,21 @@ -//! The Equihash Proof-of-Work function. +//! Equihash is a Proof-of-Work algorithm, based on a generalization of the Birthday +//! problem which finds colliding hash values. It was designed to be memory-hard; more +//! specifically, the bottle-neck for parallel implementations of Equihash solvers would +//! be memory bandwidth. +//! +//! This crate implements Equihash as specified for the Zcash consensus rules. It can +//! verify solutions for any valid `(n, k)` parameters, as long as the row indices are no +//! larger than 32 bits (that is, `ceiling(((n / (k + 1)) + 1) / 8) <= 4`). +//! +//! References +//! ========== +//! - [Section 7.6.1: Equihash.] Zcash Protocol Specification, version 2020.1.10 or later. +//! - Alex Biryukov and Dmitry Khovratovich. +//! [*Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem.*][BK16] +//! NDSS ’16. +//! +//! [Section 7.6.1: Equihash.]: https://zips.z.cash/protocol/protocol.pdf#equihash +//! [BK16]: https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf mod verify; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index e2ee033385..16159a4bf3 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -108,6 +108,7 @@ impl Node { } } +/// An Equihash solution failed to verify. #[derive(Debug)] pub struct Error(Kind); @@ -329,6 +330,8 @@ fn is_valid_solution_recursive( } } +/// Checks whether `soln` is a valid solution for `(input, nonce)` with the +/// parameters `(n, k)`. pub fn is_valid_solution( n: u32, k: u32, From 77406580848ae2dd37f0c17a1bd6f5cc87079b42 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:50:58 +1200 Subject: [PATCH 092/210] equihash: Add additional constraints on Params These are requirements of the general Equihash implementation, that are satisfied by the Zcash parameters. --- components/equihash/src/verify.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 16159a4bf3..111015c7c0 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -22,7 +22,12 @@ struct Node { impl Params { fn new(n: u32, k: u32) -> Result { - if (k < n) && (n % 8 == 0) { + // We place the following requirements on the parameters: + // - n is a multiple of 8, so the hash output has an exact byte length. + // - k >= 3 so the encoded solutions have an exact byte length. + // - k < n, so the collision bit length is at least 1. + // - n is a multiple of k + 1, so we have an integer collision bit length. + if (n % 8 == 0) && (k >= 3) && (k < n) && (n % (k + 1) == 0) { Ok(Params { n, k }) } else { Err(Error(Kind::InvalidParams)) From 72fbd2071d20a7c50ea414aa3a352eef395805b5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:51:52 +1200 Subject: [PATCH 093/210] equihash: Clarify order of operations in indices_from_minimal --- components/equihash/src/verify.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 111015c7c0..b77debca35 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -213,7 +213,8 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { fn indices_from_minimal(p: Params, minimal: &[u8]) -> Result, Error> { let c_bit_len = p.collision_bit_length(); - if minimal.len() != (1 << p.k) * (c_bit_len + 1) / 8 { + // Division is exact because k >= 3. + if minimal.len() != ((1 << p.k) * (c_bit_len + 1)) / 8 { return Err(Error(Kind::InvalidParams)); } From 1b059d4ae0494241d6ef7a7aeeecd0ede2aa4f2d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 20:53:50 +1200 Subject: [PATCH 094/210] equihash: Remove unnecessary log dependency --- components/equihash/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index edbdc4f7cd..830809558a 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -11,4 +11,3 @@ edition = "2018" [dependencies] blake2b_simd = "0.5" byteorder = "1" -log = "0.4" From 8188fae779e20d7ac1d5d8043d8ecad867c0a267 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 9 Jul 2020 23:48:09 +1200 Subject: [PATCH 095/210] zcash_client_sqlite: Implement outgoing viewing key policies This enables an SQLite light client to specify whether recipient history can be recovered from the block chain (and by what outgoing viewing key) with per-transaction granularity. --- zcash_client_sqlite/Cargo.toml | 1 + zcash_client_sqlite/src/transact.rs | 183 +++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 4 deletions(-) diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index e1fe38b22e..ec98318007 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -17,6 +17,7 @@ bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } pairing = { version = "0.16", path = "../pairing" } protobuf = "2" +rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } time = "0.1" zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index d1d9346f61..c7e9d285a1 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -2,6 +2,7 @@ use ff::PrimeField; use pairing::bls12_381::Bls12; +use rand_core::{OsRng, RngCore}; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::convert::TryInto; use std::path::Path; @@ -9,6 +10,7 @@ use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ consensus, jubjub::fs::{Fs, FsRepr}, + keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, note_encryption::Memo, primitives::{Diversifier, Note}, @@ -28,6 +30,32 @@ use crate::{ get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, }; +/// Describes a policy for which outgoing viewing key should be able to decrypt +/// transaction outputs. +/// +/// For details on what transaction information is visible to the holder of an outgoing +/// viewing key, refer to [ZIP 310]. +/// +/// [ZIP 310]: https://zips.z.cash/zip-0310 +pub enum OvkPolicy { + /// Use the outgoing viewing key from the sender's [`ExtendedFullViewingKey`]. + /// + /// Transaction outputs will be decryptable by the sender, in addition to the + /// recipients. + Sender, + + /// Use a custom outgoing viewing key. This might for instance be derived from a + /// separate seed than the wallet's spending keys. + /// + /// Transaction outputs will be decryptable by the recipients, and whoever controls + /// the provided outgoing viewing key. + Custom(OutgoingViewingKey), + + /// Use no outgoing viewing key. Transaction outputs will be decryptable by their + /// recipients, but not by the sender. + Discard, +} + struct SelectedNoteRow { diversifier: Diversifier, note: Note, @@ -43,6 +71,23 @@ struct SelectedNoteRow { /// Do not call this multiple times in parallel, or you will generate transactions that /// double-spend the same notes. /// +/// # Transaction privacy +/// +/// `ovk_policy` specifies the desired policy for which outgoing viewing key should be +/// able to decrypt the outputs of this transaction. This is primarily relevant to +/// wallet recovery from backup; in particular, [`OvkPolicy::Discard`] will prevent the +/// recipient's address, and the contents of `memo`, from ever being recovered from the +/// block chain. (The total value sent can always be inferred by the sender from the spent +/// notes and received change.) +/// +/// Regardless of the specified policy, `create_to_address` saves `to`, `value`, and +/// `memo` in `db_data`. This can be deleted independently of `ovk_policy`. +/// +/// For details on what transaction information is visible to the holder of a full or +/// outgoing viewing key, refer to [ZIP 310]. +/// +/// [ZIP 310]: https://zips.z.cash/zip-0310 +/// /// # Examples /// /// ``` @@ -50,7 +95,7 @@ struct SelectedNoteRow { /// constants::testnet::COIN_TYPE, /// keys::spending_key, /// }; -/// use zcash_client_sqlite::transact::create_to_address; +/// use zcash_client_sqlite::transact::{create_to_address, OvkPolicy}; /// use zcash_primitives::{consensus, transaction::components::Amount}; /// use zcash_proofs::prover::LocalTxProver; /// @@ -72,6 +117,7 @@ struct SelectedNoteRow { /// &to, /// Amount::from_u64(1).unwrap(), /// None, +/// OvkPolicy::Sender, /// ) { /// Ok(tx_row) => (), /// Err(e) => (), @@ -85,6 +131,7 @@ pub fn create_to_address>( to: &RecipientAddress, value: Amount, memo: Option, + ovk_policy: OvkPolicy, ) -> Result { let data = Connection::open(db_data)?; @@ -101,7 +148,20 @@ pub fn create_to_address>( { return Err(Error(ErrorKind::InvalidExtSK(account))); } - let ovk = extfvk.fvk.ovk; + + // Apply the outgoing viewing key policy. + let ovk = match ovk_policy { + OvkPolicy::Sender => extfvk.fvk.ovk, + OvkPolicy::Custom(ovk) => ovk, + OvkPolicy::Discard => { + // Generate a random outgoing viewing key that the caller does not know. + // The probability of this colliding with a legitimate outgoing viewing + // key is negligible. + let mut ovk = [0; 32]; + OsRng.fill_bytes(&mut ovk); + OutgoingViewingKey(ovk) + } + }; // Target the next block, assuming we are up-to-date. let (height, anchor_height) = { @@ -312,17 +372,20 @@ pub fn create_to_address>( #[cfg(test)] mod tests { + use rusqlite::Connection; use tempfile::NamedTempFile; use zcash_primitives::{ block::BlockHash, consensus, + note_encryption::try_sapling_output_recovery, prover::TxProver, - transaction::components::Amount, + transaction::{components::Amount, Transaction}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + JUBJUB, }; use zcash_proofs::prover::LocalTxProver; - use super::create_to_address; + use super::{create_to_address, OvkPolicy}; use crate::{ init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database}, query::{get_balance, get_verified_balance}, @@ -365,6 +428,7 @@ mod tests { &to, Amount::from_u64(1).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 0"), @@ -377,6 +441,7 @@ mod tests { &to, Amount::from_u64(1).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 1"), @@ -404,6 +469,7 @@ mod tests { &to, Amount::from_u64(1).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!(e.to_string(), "Must scan blocks first"), @@ -435,6 +501,7 @@ mod tests { &to, Amount::from_u64(1).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( @@ -499,6 +566,7 @@ mod tests { &to, Amount::from_u64(70000).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( @@ -529,6 +597,7 @@ mod tests { &to, Amount::from_u64(70000).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( @@ -556,6 +625,7 @@ mod tests { &to, Amount::from_u64(70000).unwrap(), None, + OvkPolicy::Sender, ) .unwrap(); } @@ -598,6 +668,7 @@ mod tests { &to, Amount::from_u64(15000).unwrap(), None, + OvkPolicy::Sender, ) .unwrap(); @@ -610,6 +681,7 @@ mod tests { &to, Amount::from_u64(2000).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( @@ -640,6 +712,7 @@ mod tests { &to, Amount::from_u64(2000).unwrap(), None, + OvkPolicy::Sender, ) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( @@ -667,7 +740,109 @@ mod tests { &to, Amount::from_u64(2000).unwrap(), None, + OvkPolicy::Sender, ) .unwrap(); } + + #[test] + fn ovk_policy_prevents_recovery_from_chain() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + let extsk2 = ExtendedSpendingKey::master(&[]); + let addr2 = extsk2.default_address().unwrap().1; + let to = addr2.clone().into(); + + let send_and_recover_with_policy = |ovk_policy| { + let tx_row = create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(15000).unwrap(), + None, + ovk_policy, + ) + .unwrap(); + + let data = Connection::open(db_data).unwrap(); + + // Fetch the transaction from the database + let raw_tx: Vec<_> = data + .query_row( + "SELECT raw FROM transactions + WHERE id_tx = ?", + &[tx_row], + |row| row.get(0), + ) + .unwrap(); + let tx = Transaction::read(&raw_tx[..]).unwrap(); + + // Fetch the output index from the database + let output_index: i64 = data + .query_row( + "SELECT output_index FROM sent_notes + WHERE tx = ?", + &[tx_row], + |row| row.get(0), + ) + .unwrap(); + let output = &tx.shielded_outputs[output_index as usize]; + + try_sapling_output_recovery( + &extfvk.fvk.ovk, + &output.cv, + &output.cmu, + &output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(), + &output.enc_ciphertext, + &output.out_ciphertext, + ) + }; + + // Send some of the funds to another address, keeping history. + // The recipient output is decryptable by the sender. + let (_, recovered_to, _) = send_and_recover_with_policy(OvkPolicy::Sender).unwrap(); + assert_eq!(&recovered_to, &addr2); + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 22 (that don't send us funds) + // so that the first transaction expires + for i in 1..=22 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Send the funds again, discarding history. + // Neither transaction output is decryptable by the sender. + assert!(send_and_recover_with_policy(OvkPolicy::Discard).is_none()); + } } From 8c250ca3de89faf3c7f10e44cd19cfc55faee002 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 09:49:30 +1200 Subject: [PATCH 096/210] zcash_sqlite: Add an unreliability warning to query::get_balance docs --- zcash_client_sqlite/src/query.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index 656e6924fd..c9bd5c4a92 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -34,6 +34,11 @@ pub fn get_address>(db_data: P, account: u32) -> Result Date: Fri, 10 Jul 2020 10:24:54 +1200 Subject: [PATCH 097/210] equihash 0.1.0 --- components/equihash/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index 830809558a..86bacb9e37 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "equihash" description = "The Equihash Proof-of-Work function" -version = "0.0.0" +version = "0.1.0" authors = ["Jack Grigg "] homepage = "https://github.com/zcash/librustzcash" repository = "https://github.com/zcash/librustzcash" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index bfd25d9e79..4a99d00909 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -17,7 +17,7 @@ blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.2.1" -equihash = { version = "0.0", path = "../components/equihash" } +equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" hex = "0.3" From 30fe588cca575c56bf09ae15478d843cc53730ae Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 20:35:23 +1200 Subject: [PATCH 098/210] equihash: Valid and invalid test vectors Includes existing test vectors from src/test/equihash_tests.cpp, and additional test vectors from https://github.com/zcash/zcash/pull/1487. --- components/equihash/src/lib.rs | 3 + components/equihash/src/test_vectors.rs | 5 + .../equihash/src/test_vectors/invalid.rs | 153 ++++ components/equihash/src/test_vectors/valid.rs | 841 ++++++++++++++++++ components/equihash/src/verify.rs | 196 +--- 5 files changed, 1031 insertions(+), 167 deletions(-) create mode 100644 components/equihash/src/test_vectors.rs create mode 100644 components/equihash/src/test_vectors/invalid.rs create mode 100644 components/equihash/src/test_vectors/valid.rs diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index e297308094..59c287305a 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -19,4 +19,7 @@ mod verify; +#[cfg(test)] +mod test_vectors; + pub use verify::{is_valid_solution, Error}; diff --git a/components/equihash/src/test_vectors.rs b/components/equihash/src/test_vectors.rs new file mode 100644 index 0000000000..99299fb264 --- /dev/null +++ b/components/equihash/src/test_vectors.rs @@ -0,0 +1,5 @@ +mod invalid; +mod valid; + +pub(crate) use invalid::INVALID_TEST_VECTORS; +pub(crate) use valid::VALID_TEST_VECTORS; diff --git a/components/equihash/src/test_vectors/invalid.rs b/components/equihash/src/test_vectors/invalid.rs new file mode 100644 index 0000000000..11da849e0d --- /dev/null +++ b/components/equihash/src/test_vectors/invalid.rs @@ -0,0 +1,153 @@ +use crate::verify::{Kind, Params}; + +pub(crate) struct TestVector { + pub(crate) params: Params, + pub(crate) input: &'static [u8], + pub(crate) nonce: [u8; 32], + pub(crate) solution: &'static [u32], + pub(crate) error: Kind, +} + +pub(crate) const INVALID_TEST_VECTORS: &[TestVector] = &[ + // Original valid solution: [ + // 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + // 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + // 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + // ] + + // Change one index + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2262, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::Collision, + }, + // Swap two arbitrary indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 45858, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 2261, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::Collision, + }, + // Reverse the first pair of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 15185, 2261, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::OutOfOrder, + }, + // Swap the first and second pairs of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 36112, 104243, 2261, 15185, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::OutOfOrder, + }, + // Swap the second-to-last and last pairs of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 104973, 122568, 69567, 114474, + ], + error: Kind::OutOfOrder, + }, + // Swap the first half and second half + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, + 69567, 114474, 104973, 122568, 2261, 15185, 36112, 104243, 23779, 118390, 118332, + 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, + ], + error: Kind::OutOfOrder, + }, + // Sort the indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 15972, 23460, 23779, 32642, 36112, 45858, 49807, 52426, 68190, 69567, + 69878, 76925, 80080, 80391, 81830, 85191, 90330, 91132, 92842, 104243, 104973, 111026, + 114474, 115059, 116805, 118332, 118390, 122568, 122819, 130041, + ], + error: Kind::Collision, + }, + // Duplicate indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 2261, 15185, 15185, 36112, 36112, 104243, 104243, 23779, 23779, 118390, 118390, + 118332, 118332, 130041, 130041, 32642, 32642, 69878, 69878, 76925, 76925, 80080, 80080, + 45858, 45858, 116805, 116805, 92842, 92842, 111026, 111026, + ], + error: Kind::DuplicateIdxs, + }, + // Duplicate first half + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 2261, 15185, 36112, 104243, 23779, 118390, 118332, + 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, + ], + error: Kind::DuplicateIdxs, + }, +]; diff --git a/components/equihash/src/test_vectors/valid.rs b/components/equihash/src/test_vectors/valid.rs new file mode 100644 index 0000000000..a55de1b96a --- /dev/null +++ b/components/equihash/src/test_vectors/valid.rs @@ -0,0 +1,841 @@ +use crate::verify::Params; + +pub(crate) struct TestVector { + pub(crate) params: Params, + pub(crate) input: &'static [u8], + pub(crate) nonce: [u8; 32], + pub(crate) solutions: &'static [&'static [u32]], +} + +pub(crate) const VALID_TEST_VECTORS: &[TestVector] = &[ + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [0; 32], + solutions: &[ + &[ + 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, + 129167, 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, + 36473, 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, + ], + &[ + 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, + 123026, 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, + 94769, 118158, 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, + ], + &[ + 1278, 107636, 80519, 127719, 19716, 130440, 83752, 121810, 15337, 106305, 96940, + 117036, 46903, 101115, 82294, 118709, 4915, 70826, 40826, 79883, 37902, 95324, + 101092, 112254, 15536, 68760, 68493, 125640, 67620, 108562, 68035, 93430, + ], + &[ + 3976, 108868, 80426, 109742, 33354, 55962, 68338, 80112, 26648, 28006, 64679, + 130709, 41182, 126811, 56563, 129040, 4013, 80357, 38063, 91241, 30768, 72264, + 97338, 124455, 5607, 36901, 67672, 87377, 17841, 66985, 77087, 85291, + ], + &[ + 5970, 21862, 34861, 102517, 11849, 104563, 91620, 110653, 7619, 52100, 21162, + 112513, 74964, 79553, 105558, 127256, 21905, 112672, 81803, 92086, 43695, 97911, + 66587, 104119, 29017, 61613, 97690, 106345, 47428, 98460, 53655, 109002, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, + 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, + 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 165, 27290, 87424, 123403, 5344, 35125, 49154, 108221, 8882, 90328, 77359, 92348, + 54692, 81690, 115200, 121929, 18968, 122421, 32882, 128517, 56629, 88083, 88022, + 102461, 35665, 62833, 95988, 114502, 39965, 119818, 45010, 94889, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 1855, 37525, 81472, 112062, 11831, 38873, 45382, 82417, 11571, 47965, 71385, + 119369, 13049, 64810, 26995, 34659, 6423, 67533, 88972, 105540, 30672, 80244, + 39493, 94598, 17858, 78496, 35376, 118645, 50186, 51838, 70421, 103703, + ], + &[ + 3671, 125813, 31502, 78587, 25500, 83138, 74685, 98796, 8873, 119842, 21142, 55332, + 25571, 122204, 31433, 80719, 3955, 49477, 4225, 129562, 11837, 21530, 75841, + 120644, 4653, 101217, 19230, 113175, 16322, 24384, 21271, 96965, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 2570, 20946, 61727, 130667, 16426, 62291, 107177, 112384, 18464, 125099, 120313, + 127545, 35035, 73082, 118591, 120800, 13800, 32837, 23607, 86516, 17339, 114578, 22053, + 85510, 14913, 42826, 25168, 121262, 33673, 114773, 77592, 83471, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [0; 32], + solutions: &[ + &[ + 3130, 83179, 30454, 107686, 71240, 88412, 109700, 114639, 10024, 32706, 38019, + 113013, 18399, 92942, 21094, 112263, 4146, 30807, 10631, 73192, 22216, 90216, + 45581, 125042, 11256, 119455, 93603, 110112, 59851, 91545, 97403, 111102, + ], + &[ + 3822, 35317, 47508, 119823, 37652, 117039, 69087, 72058, 13147, 111794, 65435, + 124256, 22247, 66272, 30298, 108956, 13157, 109175, 37574, 50978, 31258, 91519, + 52568, 107874, 14999, 103687, 27027, 109468, 36918, 109660, 42196, 100424, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, + 80080, 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, + 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + &[ + 16700, 46276, 21232, 43153, 22398, 58511, 47922, 71816, 23370, 26222, 39248, 40137, + 65375, 85794, 69749, 73259, 23599, 72821, 42250, 52383, 35267, 75893, 52152, 57181, + 27137, 101117, 45804, 92838, 29548, 29574, 37737, 113624, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 6005, 59843, 55560, 70361, 39140, 77856, 44238, 57702, 32125, 121969, 108032, 116542, + 37925, 75404, 48671, 111682, 6937, 93582, 53272, 77545, 13715, 40867, 73187, 77853, + 7348, 70313, 24935, 24978, 25967, 41062, 58694, 110036, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 968, 90691, 70664, 112581, 17233, 79239, 66772, 92199, 27801, 44198, 58712, 122292, + 28227, 126747, 70925, 118108, 2876, 76082, 39335, 113764, 26643, 60579, 50853, + 70300, 19640, 31848, 28672, 87870, 33574, 50308, 40291, 61593, + ], + &[ + 1181, 61261, 75793, 96302, 36209, 113590, 79236, 108781, 8275, 106510, 11877, + 74550, 45593, 80595, 71247, 95783, 2991, 99117, 56413, 71287, 10235, 68286, 22016, + 104685, 51588, 53344, 56822, 63386, 63527, 75772, 93100, 108542, + ], + &[ + 2229, 30387, 14573, 115700, 20018, 124283, 84929, 91944, 26341, 64220, 69433, + 82466, 29778, 101161, 59334, 79798, 2533, 104985, 50731, 111094, 10619, 80909, + 15555, 119911, 29028, 42966, 51958, 86784, 34561, 97709, 77126, 127250, + ], + &[ + 15465, 59017, 93851, 112478, 24940, 128791, 26154, 107289, 24050, 78626, 51948, + 111573, 35117, 113754, 36317, 67606, 21508, 91486, 28293, 126983, 23989, 39722, + 60567, 97243, 26720, 56243, 60444, 107530, 40329, 56467, 91943, 93737, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 1120, 77433, 58243, 76860, 11411, 96068, 13150, 35878, 15049, 88928, 20101, 104706, + 29215, 73328, 39498, 83529, 9233, 124174, 66731, 97423, 10823, 92444, 25647, 127742, + 12207, 46292, 22018, 120758, 14411, 46485, 21828, 57591, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Test case with 3+-way collision in the final round.", + nonce: [ + 0xf0, 0x07, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + ], + solutions: &[ + &[ + 1162, 129543, 57488, 82745, 18311, 115612, 20603, 112899, 5635, 103373, 101651, + 125986, 52160, 70847, 65152, 101720, 5810, 43165, 64589, 105333, 11347, 63836, + 55495, 96392, 40767, 81019, 53976, 94184, 41650, 114374, 45109, 57038, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 58515, 78703, 100100, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 78703, 58515, 100100, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 100100, 58515, 78703, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 4488, 83544, 24912, 62564, 43206, 62790, 68462, 125162, 6805, 8886, 46937, 54588, + 15509, 126232, 19426, 27845, 5959, 56839, 38806, 102580, 11255, 63258, 23442, + 39750, 13022, 22271, 24110, 52077, 17422, 124996, 35725, 101509, + ], + &[ + 8144, 33053, 33933, 77498, 21356, 110495, 42805, 116575, 27360, 48574, 100682, + 102629, 50754, 64608, 96899, 120978, 11924, 74422, 49240, 106822, 12787, 68290, + 44314, 50005, 38056, 49716, 83299, 95307, 41798, 82309, 94504, 96161, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [0; 32], + solutions: &[&[ + 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, + 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, + 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, + 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, + 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, + 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, + 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, + 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, + 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, + 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, + 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, + 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, + 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, + 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, + 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, + 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, + 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, + 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, + 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, + 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, + 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, + 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, + 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, + 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, + 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, + 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, + 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, + 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, + 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, + 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, + 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, + 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, + 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, + 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, + 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, + 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, + 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, + 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, + 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, + 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, + 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, + 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, + 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, + 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, + 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, + 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, + 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, + 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, + 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, + 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, + 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, + 1971986, + ]], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, + 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, + 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, + 1068055, 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, + 1117445, 1936058, 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, + 21678, 822781, 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, + 934427, 1068834, 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, + 1865216, 45723, 1820952, 1160970, 1585983, 422549, 1973097, 1296271, 2006382, + 650084, 809838, 871727, 1080419, 28500, 1471829, 384406, 619459, 212041, 1466258, + 481435, 866461, 145340, 1403843, 1339592, 1405761, 163425, 1073771, 285027, + 1488210, 167744, 1182267, 1354059, 2089602, 921700, 2059931, 1704721, 1853088, + 585171, 739246, 747551, 1520527, 590255, 1175747, 705292, 998433, 522014, 1931179, + 1629531, 1692879, 588830, 1799457, 963672, 1664237, 775408, 1926741, 907030, + 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, 88704, 1302687, 579526, + 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, 1521894, 1917599, + 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, 1316156, + 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, 1565477, + 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, 1144365, + 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, + 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, + 962900, 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, + 862432, 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, + 54984, 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, + 1403350, 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, + 456321, 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, + 1156906, 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, + 139460, 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, + 1514260, 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, + 1972064, 254647, 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, + 1768467, 853790, 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, + 920754, 1507358, 12883, 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, + 18523, 1236125, 669105, 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, + 985912, 2091029, 84065, 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, + 288960, 861994, 622074, 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, + 851053, 1715626, 531385, 1213667, 1093995, 1863757, 630365, 1851894, 1328101, + 1770446, 31900, 734027, 1078651, 1701535, 123276, 1916343, 581822, 1681706, 573135, + 818091, 1454710, 2052521, 1150284, 1451159, 1482280, 1811430, 26321, 785837, + 877980, 2073103, 107324, 727248, 1785460, 1840517, 184560, 185640, 364103, 1878753, + 518459, 1984029, 964109, 1884200, 74003, 527272, 516232, 711247, 148582, 209254, + 634610, 1534140, 376714, 1573267, 421225, 1265101, 1078858, 1374310, 1806283, + 2091298, 23392, 389637, 413663, 1066737, 226164, 762552, 1048220, 1583397, 40092, + 277435, 775449, 1533894, 202582, 390703, 346741, 1027320, 523034, 809424, 584882, + 1296934, 528062, 733331, 1212771, 1958651, 653372, 1313962, 1366332, 1784489, + 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, 1698486, 57289, + 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, 1285209, 265623, + 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, 1296171, 158847, + 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, 133251, 1136222, + 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, 1457135, + 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, + 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, + 1137531, 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, + 1939366, 232907, 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, + 229793, 1576982, 1420059, 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, + 1012424, 1428921, 890113, 1822645, 1082368, 1392894, + ], + &[ + 13396, 1502141, 934546, 1419227, 445798, 1676403, 643830, 1421927, 45286, 1160795, + 117864, 542369, 501065, 1834465, 544881, 1258964, 157233, 888851, 1707333, 2042954, + 1067373, 1959382, 1081841, 1528092, 355787, 1506512, 488244, 1901282, 842029, + 1045169, 1014084, 1718668, 184257, 1419101, 572200, 1554883, 240034, 1489590, + 1108495, 1346106, 357644, 1206700, 1019151, 1817512, 827374, 945127, 885925, + 1320873, 292347, 918571, 436037, 973478, 321703, 1853464, 595802, 894629, 426733, + 849916, 1618173, 1877920, 1260413, 1913655, 1413450, 1821154, 39823, 934676, + 1415329, 1899092, 248682, 1500533, 603937, 2061626, 126829, 490133, 1491924, + 1591619, 474135, 1328233, 878943, 2058462, 86988, 911439, 767925, 1499098, 323431, + 335282, 1398512, 1561768, 142731, 1568252, 459703, 1378588, 443971, 730680, 723742, + 1301913, 120606, 402225, 659692, 878779, 967663, 1022156, 1573638, 1710098, 228789, + 483630, 565528, 1137688, 569834, 1100519, 1315274, 1613960, 323958, 1035040, + 342578, 1395004, 693886, 778875, 1128411, 1424459, 470124, 1001591, 1265290, + 1962023, 898247, 1156840, 1424257, 1463363, 29922, 1694756, 109027, 1777528, + 115450, 1525306, 1347575, 1835714, 279549, 1320423, 1162056, 1668629, 934706, + 1283078, 1115477, 1231121, 60882, 1677375, 729195, 1560584, 199789, 770485, 532152, + 1939788, 225610, 1517013, 1952643, 2085389, 453439, 1202000, 902342, 1303946, + 208859, 493534, 1185220, 1943780, 639093, 1123038, 1247020, 1359794, 354070, + 1207821, 463570, 650864, 846889, 875335, 1220142, 1929715, 222307, 849285, 859051, + 1961534, 419719, 1330637, 441693, 1566326, 590324, 1106956, 1198428, 1208601, + 1194514, 1714365, 1549673, 1766871, 36406, 315544, 313443, 1553541, 769731, + 1608444, 1457538, 1893815, 192649, 1030632, 282298, 846640, 287483, 336140, 541071, + 1079100, 139955, 404958, 1306603, 1734441, 843559, 1448208, 1273901, 1715116, + 547237, 1060287, 629301, 1036790, 733471, 1236082, 1073097, 1137292, 78721, 365289, + 321250, 730155, 505572, 1570719, 1272465, 1762855, 200602, 2002477, 376698, 586952, + 574825, 1774405, 1739907, 1910482, 321686, 1711532, 514504, 769967, 419504, + 1565290, 586517, 1474371, 394689, 802951, 1529816, 2003281, 1025904, 1789059, + 1557849, 1649570, 22351, 1020349, 799545, 1167424, 372784, 822777, 1331906, + 1487316, 147912, 779616, 503790, 591742, 831949, 1009009, 846119, 1154281, 160278, + 1306827, 972322, 1632998, 925417, 1862659, 1167562, 1487753, 225867, 1170797, + 595379, 1116885, 580973, 823382, 825674, 1756426, 123619, 578961, 1105801, 1478751, + 615784, 1248585, 1555596, 1823147, 291494, 1439342, 1362238, 2020172, 416541, + 808362, 1087192, 1279428, 145445, 1476821, 589962, 1007778, 502785, 578289, + 1347895, 1468692, 397887, 1422580, 873197, 1547340, 544937, 1706334, 1240996, + 1394517, 60961, 173017, 1198379, 1875710, 164983, 1244552, 786241, 1163907, 182496, + 445793, 765554, 1044380, 274547, 1626395, 1621315, 1632207, 168375, 924818, + 1355514, 1888896, 1060612, 1309397, 1247169, 2065604, 537737, 849561, 690965, + 1778198, 834911, 1341821, 1239610, 1468591, 90037, 516053, 906189, 1302107, 544896, + 1319300, 1944402, 2080316, 332213, 469952, 949830, 1187792, 400278, 459757, + 1306417, 1772258, 135309, 995973, 571189, 2041352, 375936, 389143, 1350844, + 1611928, 294058, 577712, 820473, 2062905, 435690, 1918441, 1160042, 1437969, 60304, + 817774, 956977, 1785565, 116153, 479573, 1291516, 1468918, 260814, 1568016, 695030, + 1082881, 1175273, 1679631, 1219376, 1680268, 173493, 2060876, 1391045, 1554572, + 795587, 931051, 1528751, 1810166, 1049000, 1089918, 1780849, 2080247, 1409007, + 1720471, 1726930, 2048510, 202301, 1229629, 910467, 1501208, 249207, 758814, + 386350, 1680387, 259000, 824989, 268126, 416050, 304924, 1411322, 534678, 883265, + 211439, 1915547, 1954195, 1987528, 682149, 1798395, 766421, 1964526, 345778, + 739906, 1698586, 1980612, 1207132, 1863904, 1586749, 1798519, 89627, 322371, + 1448894, 1821630, 771946, 845162, 1627288, 1848711, 247727, 1578777, 425393, + 1141980, 738940, 2054340, 1156205, 1662664, 315398, 956023, 867847, 935583, 559412, + 1538514, 1291871, 1371526, 925807, 2038575, 1099192, 1213875, 989415, 1590364, + 1137142, 1456911, 148404, 1829533, 171283, 355992, 231384, 1712830, 380050, + 1531904, 205155, 785766, 794141, 1821460, 536969, 826105, 915018, 1727720, 473495, + 575484, 1511034, 1752204, 1030559, 1173930, 1566670, 1684100, 545434, 1644431, + 838096, 1830099, 714438, 1058571, 986457, 1275490, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 85, 2041581, 739509, 1038120, 95814, 1449199, 566808, 1970271, 22351, 1033277, + 351539, 378679, 370613, 1217658, 744902, 2054863, 128384, 2048133, 1422405, + 1711301, 266020, 338919, 1851784, 1923279, 344519, 939493, 1254831, 1365416, + 658643, 1827109, 742476, 2019543, 7557, 1416156, 42164, 1108616, 1324398, 1502720, + 1471471, 1734206, 51676, 532090, 634806, 1747514, 481844, 1488478, 690106, 1838033, + 93690, 1442016, 977262, 1136782, 239698, 1964439, 1032494, 2041403, 463135, + 1204579, 693303, 1522068, 880410, 2021579, 1108504, 1718764, 3462, 1916805, + 1727074, 1789966, 562318, 1651780, 1332270, 1995649, 295751, 2023013, 1119902, + 1690352, 1293091, 2056850, 1974345, 2044869, 78574, 899703, 1106267, 1286448, + 1303134, 1850087, 1355112, 1776010, 1031239, 1851498, 1153488, 1243952, 1163993, + 1977728, 1328544, 1612102, 9487, 233220, 998029, 1173368, 549226, 2073453, 871154, + 1572100, 46216, 739886, 1234167, 1572986, 374817, 878325, 910917, 1079476, 9548, + 961914, 1057590, 1411096, 973096, 1060957, 1188074, 1721366, 465242, 2055339, + 971225, 1830281, 526459, 2042659, 746133, 1985292, 7405, 1510070, 385903, 2095485, + 468941, 1679477, 757944, 1622263, 246823, 695851, 444054, 846202, 321170, 1678719, + 928172, 1531270, 13258, 342299, 639214, 1919221, 412214, 430924, 787608, 1968276, + 32804, 791991, 524319, 1083379, 568152, 1875970, 753609, 1958222, 44322, 324266, + 1072444, 1182703, 133944, 1208050, 900653, 1614070, 373367, 1363285, 663351, + 1459703, 578444, 1419137, 1163520, 1922722, 65157, 1631833, 1034031, 1487396, + 723173, 1724173, 1482982, 1644877, 384747, 909984, 1275503, 2036514, 610392, + 1093084, 913780, 1924334, 11137, 1546273, 61787, 295562, 319377, 2057614, 1229059, + 2010647, 209286, 1287454, 1013313, 1747506, 271940, 1520544, 1018674, 1063669, + 185227, 1219872, 1288529, 1548657, 344601, 1898125, 1755668, 1992858, 890818, + 1100957, 1565899, 1575128, 1207190, 1821158, 1999048, 2022807, 19362, 2055304, + 757990, 2088728, 478320, 1006345, 509532, 1966851, 160002, 648308, 414679, 1022972, + 528460, 1898952, 919894, 1918492, 154904, 1997802, 1528735, 1687070, 240714, + 1414676, 1400402, 1763165, 381766, 1044133, 619868, 1519386, 1248422, 1409298, + 1754871, 2015118, 1739, 499886, 1642104, 2069348, 437356, 609873, 491378, 1137963, + 89811, 1626714, 873752, 1548730, 1114856, 1941590, 1481869, 1625018, 59629, 668173, + 315591, 733560, 803171, 1801431, 1294776, 1914531, 253597, 1771037, 650342, + 1014718, 375289, 519529, 1447780, 1900126, 8241, 1229781, 777968, 1198408, 104296, + 2030372, 683340, 1454000, 91445, 100079, 645496, 824897, 392258, 1740230, 1525343, + 2069444, 110826, 1097701, 1069615, 1960595, 530572, 1028831, 999251, 1458171, + 146008, 1135021, 867825, 1398554, 397922, 818160, 587611, 1867232, 11088, 414753, + 572774, 2060307, 407170, 687100, 1002378, 1924055, 225264, 1608839, 792486, + 1925598, 470948, 519691, 700762, 1434860, 164901, 1277475, 377305, 1816065, 526937, + 1419265, 639397, 690184, 259943, 444998, 672324, 836053, 601877, 1693911, 1108479, + 1809555, 147947, 796744, 732775, 1441222, 325070, 1809776, 1873763, 2013982, + 481882, 1288648, 1653390, 1654906, 532739, 2062844, 758222, 1372565, 339507, + 1224640, 1392890, 1850326, 1130365, 1924596, 1177208, 1363642, 384241, 515152, + 1164040, 2004909, 609791, 1575213, 1671915, 1691266, 3039, 1774544, 200172, 273877, + 420816, 737235, 986055, 1164239, 165598, 265509, 1009133, 2062342, 758743, 1489470, + 1260158, 1924360, 208628, 1135455, 794209, 1067104, 469480, 1795800, 1183662, + 1360938, 335183, 822888, 831116, 2088169, 399584, 1836326, 1174096, 2034335, 95734, + 1427706, 1593344, 2070787, 305103, 459806, 1134106, 1581586, 304533, 1761123, + 454382, 1620968, 974160, 1661165, 1984968, 2006168, 143936, 1576427, 1420916, + 2050868, 239423, 1955755, 713829, 1553644, 613116, 653092, 957406, 1332874, 634343, + 1504804, 1539492, 1652920, 29255, 84313, 134872, 1722963, 936125, 1636028, 1518342, + 1910113, 74089, 1517035, 141099, 1837859, 91886, 1841153, 483590, 1276988, 94868, + 209194, 613253, 1062768, 289463, 1150432, 1216070, 2086920, 226473, 1630691, + 482394, 1837175, 389596, 2002601, 395772, 870173, 43107, 688649, 936340, 1235157, + 189041, 1855656, 597803, 1251423, 472775, 1688197, 1286637, 1760949, 930937, + 1072689, 1187497, 1784673, 58620, 1436417, 146777, 1677387, 66982, 746844, 945993, + 1703252, 347963, 945075, 445864, 1694069, 946355, 1646534, 1769893, 1806674, + ], + &[ + 2280, 1675737, 1241864, 1426081, 529325, 1356538, 1546188, 2018466, 113218, + 1885133, 750288, 1896938, 785567, 802607, 1597047, 1985969, 132292, 1612427, + 551147, 1732380, 1140541, 1246254, 1371957, 1567864, 369405, 1582447, 1726106, + 1947007, 435161, 1369789, 928581, 1556123, 241507, 1653097, 395601, 975278, 633072, + 1541996, 1250446, 1740729, 545122, 1930170, 980220, 1098305, 1158689, 1369613, + 1570322, 1726377, 438386, 783277, 968764, 1057094, 559257, 1187476, 1228488, + 2074064, 813312, 1810708, 1064164, 2087729, 923610, 1552562, 1327854, 1735362, + 32630, 1981975, 310780, 1158178, 263597, 363824, 2052751, 2073086, 45706, 847451, + 584418, 813295, 716033, 968866, 854478, 1868422, 594854, 1676760, 1410774, 2055426, + 661611, 1138770, 997952, 1309381, 984769, 1370253, 1649486, 1920232, 1080026, + 1654268, 1212179, 1834060, 58487, 971078, 424085, 1175474, 687490, 1317807, 713352, + 1985958, 142307, 1854880, 1309882, 1540711, 487396, 904606, 975430, 1385196, 88528, + 1099752, 372012, 1708451, 207227, 1674648, 1476751, 1547086, 138190, 738504, + 779891, 1107444, 505099, 1265858, 613613, 1884841, 17621, 1157648, 209013, 526174, + 971607, 1004381, 1202861, 1494745, 274131, 982841, 729228, 886096, 478622, 1293202, + 539968, 885395, 152578, 1647348, 494562, 1327036, 342817, 1698049, 725707, 1547591, + 767029, 1290077, 1546025, 1736585, 1219491, 1852307, 1555669, 1883327, 61829, + 813247, 482047, 1362746, 93496, 1467091, 1070897, 1559668, 305281, 690664, 326883, + 444914, 443937, 1762042, 614124, 1309010, 148533, 1571755, 497978, 2074730, + 1545845, 1666088, 1757232, 1900305, 189111, 802199, 203091, 881152, 926582, + 1675352, 1478644, 1677015, 20277, 435117, 396319, 834104, 370396, 1445594, 1161835, + 2054329, 230752, 644229, 568858, 1963813, 872483, 974796, 984693, 1105289, 94040, + 1781444, 278075, 1901607, 165376, 834499, 951353, 1932215, 146197, 664541, 383107, + 1743858, 287583, 1810757, 570459, 1739938, 24296, 1527677, 1742586, 2000245, + 392111, 1596429, 915955, 1501951, 590908, 1689547, 752882, 1853220, 749276, + 1941184, 1507459, 1578707, 167457, 1313516, 428750, 1165032, 324442, 368759, + 1283775, 2017161, 364763, 2053902, 905316, 1800528, 581102, 2050600, 818573, + 1762293, 2711, 622959, 813485, 831354, 41471, 1324329, 862647, 1007821, 26246, + 1120632, 1156719, 1215948, 907527, 1144579, 1042530, 1287508, 138588, 1538967, + 1265927, 1735492, 229904, 1039415, 999131, 1826370, 392686, 730051, 623787, + 2047295, 409449, 1711230, 462385, 1992127, 42656, 880450, 42729, 1973289, 479692, + 1993383, 673152, 1885841, 170786, 748129, 521360, 818751, 982929, 1234321, 1113308, + 1907714, 98953, 1728051, 947493, 1289327, 199255, 1474247, 681622, 1416365, 352728, + 1796749, 603909, 811763, 946618, 1104470, 973691, 1152770, 21297, 639834, 594075, + 1017433, 133138, 630545, 537386, 1347734, 365214, 844135, 1341949, 1460424, 673293, + 758707, 754735, 1456846, 40018, 993218, 91092, 1539083, 1526483, 1757364, 1595990, + 1598593, 230413, 1897770, 538469, 1395634, 396644, 1893699, 442435, 571726, 52013, + 1125704, 1098095, 2080341, 200259, 2046061, 1241415, 1755598, 573138, 735184, + 795832, 1567959, 680511, 1310894, 1103029, 1516884, 135655, 1326571, 692790, + 1190347, 397690, 1163996, 793521, 1677407, 307698, 1644146, 476490, 480901, 939619, + 1485684, 1154733, 1255691, 3690, 219420, 282792, 439050, 145272, 576914, 712049, + 1304645, 573928, 1772866, 591113, 1393852, 1035141, 1950385, 1660238, 2048950, + 156755, 1608179, 1548683, 1672961, 305784, 1862688, 321105, 1025297, 452008, + 1005531, 843560, 1190325, 856621, 1117370, 1130365, 1527816, 49720, 747725, 88191, + 532153, 387221, 2046361, 562281, 1174973, 58051, 665523, 1087404, 1953295, 288049, + 2095453, 843437, 1391132, 71028, 684791, 905101, 983353, 869595, 952262, 1639398, + 1914119, 419401, 1543571, 1072120, 1589356, 1156347, 1293818, 1377687, 1462379, + 15074, 1544101, 82479, 277248, 608805, 980975, 627710, 2071408, 210587, 2050837, + 837017, 1832082, 1225589, 1331877, 1546243, 1710461, 263463, 1118404, 1070140, + 1965733, 573307, 1351499, 983779, 1981322, 606434, 1595416, 765946, 1106775, + 909539, 1338917, 1288935, 1474260, 360272, 448135, 848529, 1128620, 425058, + 1408729, 552440, 908024, 536460, 1031151, 1355099, 1690286, 758818, 965662, + 1568718, 1811172, 394608, 1421319, 528521, 983018, 1098180, 2033658, 1555936, + 2016847, 558752, 1627758, 1319354, 1973293, 844477, 2092434, 1252290, 1572207, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 4864, 1199590, 193812, 1249880, 714671, 1837456, 1535571, 1708457, 232772, 1454167, + 457525, 637002, 332079, 1501386, 718273, 1615866, 77066, 484710, 167020, 1302858, + 478350, 1147611, 1080786, 1765391, 192941, 989350, 946203, 2030362, 741168, + 1308632, 981242, 1278097, 37040, 342963, 569456, 668433, 603930, 933078, 698729, + 1622250, 64884, 1692110, 592064, 1876950, 1033063, 1611191, 1595251, 1620759, + 85174, 1366524, 780855, 1565061, 499859, 1083320, 659353, 2024776, 245240, 653208, + 610593, 1607404, 692246, 1235766, 1256907, 1797417, 50473, 131951, 1598065, + 1624695, 1111152, 1515152, 1388781, 1891337, 160469, 432196, 393547, 812762, + 176550, 1413868, 509501, 1271887, 318995, 1437575, 991689, 2006157, 539568, + 1719313, 794460, 1908039, 535401, 1235620, 1455510, 1626953, 1091062, 1782601, + 1467126, 1580102, 170783, 1348866, 1353402, 1491258, 772707, 1966772, 826506, + 1152761, 449546, 1960316, 1722182, 2080464, 585763, 1674095, 609247, 843509, + 172788, 1720636, 435897, 1922357, 500566, 1040796, 613483, 1370281, 256465, + 1230426, 769634, 1233698, 338802, 1256346, 1127412, 1606391, 28572, 1971791, + 1444448, 1918707, 97182, 1965648, 257599, 1202977, 93470, 966723, 905279, 1233897, + 113192, 1584168, 1161311, 2032889, 256253, 1722369, 1073588, 1855590, 346680, + 1584579, 1743391, 1934763, 312281, 1549608, 994193, 1602606, 387880, 725528, + 934703, 1266574, 65484, 576232, 286914, 1159487, 67947, 120057, 1292895, 1703617, + 78516, 561032, 324727, 964901, 207979, 1061901, 669782, 1333235, 206098, 1887692, + 556487, 1825223, 311381, 1950933, 346751, 900814, 428287, 460877, 839356, 2060346, + 812004, 1046964, 1855974, 2094549, 104606, 743195, 954206, 1215739, 741600, + 1861587, 743395, 1346010, 494866, 1045735, 519272, 678086, 1500989, 1559455, + 1804386, 1954175, 125590, 1395720, 187846, 573761, 537850, 1166248, 1075068, + 1546494, 227070, 2020913, 1609765, 1905314, 387408, 1087195, 506841, 1499006, + 139388, 1837001, 877345, 1809095, 1352203, 1418350, 1850889, 1990899, 280451, + 1765615, 510671, 1352059, 764606, 1764448, 926933, 1636061, 211736, 1525591, + 655004, 1579428, 751390, 1914755, 1108881, 1741132, 312225, 1280938, 1278149, + 1812658, 1150817, 1667245, 1332384, 1891064, 9140, 1966916, 777375, 1545630, + 490282, 1629876, 1169924, 1800146, 343120, 369449, 1598222, 1975938, 1275395, + 1417808, 1349883, 1709635, 248136, 1064213, 248531, 419620, 395583, 934164, 957401, + 1441780, 436644, 1282126, 1185318, 1225662, 793359, 1337722, 1738888, 1910757, + 77536, 946595, 193413, 1714318, 245074, 944172, 433226, 1320914, 251395, 474279, + 837087, 1103199, 297673, 606529, 657996, 1316189, 115773, 1039045, 677050, 1570537, + 293787, 644796, 1550861, 1957774, 601959, 1591943, 1745446, 2025220, 1240950, + 1633250, 1879694, 2058372, 60446, 1323729, 575091, 1342903, 807201, 1631118, + 980227, 1144536, 105511, 2067126, 618548, 1233793, 676117, 1129668, 1416791, + 1774930, 106539, 1150242, 1145224, 1200128, 805479, 1751155, 995027, 1982253, + 767482, 1244117, 944753, 2047440, 1095507, 1299247, 1460212, 2038990, 88843, + 530639, 646346, 1179485, 1044312, 1797756, 1456669, 1549970, 496644, 1959677, + 822589, 1619867, 671946, 2071372, 839018, 1504389, 115789, 175266, 1467790, + 1972030, 1513638, 1832563, 1563718, 1897413, 203986, 391698, 1340000, 1715426, + 1050764, 1249824, 1276431, 2015035, 62131, 1545007, 525280, 717401, 177592, 661746, + 348866, 1689238, 420259, 632760, 1244376, 1935559, 465968, 1601759, 1732708, + 1760778, 99049, 302037, 183591, 822581, 196077, 680305, 1585363, 2091013, 383849, + 437456, 571005, 1266155, 611968, 1457147, 868500, 1231186, 147047, 1237994, 240469, + 1095717, 267068, 1421991, 431598, 1915282, 445827, 525511, 1327175, 1774033, + 716456, 1233031, 1061413, 1122461, 206655, 572078, 924087, 1310789, 684611, + 1287277, 1773797, 1913313, 309030, 1878386, 473275, 1003742, 1260118, 1576334, + 1318332, 1864421, 121871, 254982, 1668894, 1981433, 720578, 1301056, 935612, + 1117955, 497019, 1086159, 879102, 2090471, 872907, 1471132, 1837356, 1946140, + 132945, 1223450, 983306, 1258235, 211937, 884317, 776845, 1230188, 256819, 1715032, + 1175550, 1373727, 546648, 1594562, 1079893, 2013428, 121905, 419443, 1270708, + 2001104, 231117, 1687963, 581051, 755823, 150994, 201680, 277505, 1571629, 210684, + 267883, 346662, 1369722, 210410, 450237, 889519, 1927159, 428761, 1261947, 1112506, + 2072336, 328126, 919149, 422720, 523141, 480263, 948885, 888698, 1228888, + ], + &[ + 23901, 617413, 355244, 1975506, 132019, 973669, 1226248, 1573681, 122421, 1065428, + 582124, 909665, 558651, 1926639, 991115, 1811101, 53484, 471380, 1166430, 1172099, + 768898, 1980136, 1308533, 2043797, 225770, 1560246, 282139, 1511423, 252319, + 1591786, 1281863, 1792727, 62057, 1262311, 152547, 1040143, 854194, 1671967, + 1041149, 1607891, 65257, 1134934, 1022871, 1271798, 1447250, 1983171, 1491959, + 1874069, 179856, 1219906, 1267725, 1654491, 735071, 939234, 766437, 900743, 430964, + 2070358, 1146633, 1654102, 694284, 1822630, 762226, 1448725, 43801, 1286582, + 1375355, 2031392, 904812, 1027586, 1334970, 1596137, 1178798, 1845585, 2024912, + 2075007, 1327104, 1704913, 1924277, 2050791, 213166, 509295, 797674, 1025109, + 844789, 1735309, 1228897, 1966440, 726212, 789029, 853855, 1647548, 924001, + 1849322, 1783876, 2001548, 302290, 1172889, 501308, 2003083, 742289, 1147883, + 808789, 1707834, 681978, 1238187, 1467872, 1679417, 854573, 1431629, 2053677, + 2085589, 473258, 1062845, 1237630, 1794734, 1037815, 2094316, 1495253, 1743060, + 772774, 1993150, 1098941, 1165463, 812798, 845435, 1604066, 1982214, 36051, 613993, + 1171428, 1458641, 282798, 328217, 1168775, 1871314, 238601, 1759957, 1307623, + 1970904, 281444, 294479, 348563, 1135891, 88431, 1319495, 453854, 657030, 343548, + 1764131, 1061870, 1704218, 260179, 345854, 1307098, 1823763, 602183, 1577697, + 1809954, 2027412, 51269, 57467, 1325003, 1335582, 1311661, 1392464, 1684355, + 2053786, 534916, 1468056, 1606406, 1802740, 1162295, 1458329, 1580464, 1802771, + 62963, 1265622, 1079825, 1523318, 697498, 1137974, 1153192, 1248533, 129604, + 1178809, 1073871, 1104755, 311330, 517501, 535836, 1379634, 40198, 647811, 675812, + 936605, 486329, 765127, 775567, 1414124, 613964, 1577815, 683996, 1689517, 996979, + 2083259, 1556180, 2058600, 210016, 1789429, 275149, 2088979, 726542, 1285888, + 939385, 1301015, 305279, 339990, 307825, 523852, 332361, 1577829, 914883, 1501568, + 67628, 390418, 842350, 1733296, 1075292, 1541991, 1118111, 1412896, 133657, 421233, + 334008, 1150183, 617485, 1236606, 787610, 1451290, 179174, 1512540, 554916, + 1239556, 543320, 623168, 668527, 836286, 288147, 724320, 632809, 1666001, 989270, + 1332112, 1491347, 1690153, 47022, 628361, 1666493, 1972048, 521980, 1695996, + 1230445, 1557971, 107339, 1669410, 441113, 1464999, 675537, 2082149, 967630, + 1727034, 73959, 1283937, 1221350, 1586488, 532533, 2054356, 1372304, 1965748, + 276873, 623244, 439869, 527242, 532370, 861699, 1128671, 1814005, 81006, 451273, + 906992, 1525688, 892238, 1673990, 1173231, 1289145, 361337, 1728127, 1240059, + 1744405, 800364, 1979466, 1166409, 1340859, 185949, 556989, 600344, 692557, 316208, + 1531398, 545306, 1748454, 225612, 1139376, 646851, 1064772, 1272849, 1363216, + 1646428, 1770462, 54905, 1005365, 805801, 1395824, 75735, 1795449, 912629, 1502495, + 188676, 351070, 828616, 1590802, 406154, 1290478, 1172204, 1600863, 130475, 598371, + 612509, 784904, 489833, 563167, 504536, 1887655, 963575, 1812104, 1618265, 1771485, + 982915, 1024970, 1728069, 1928492, 83664, 988553, 347128, 1880498, 212168, 1305561, + 1862267, 1899554, 194973, 1507295, 940166, 1752636, 251583, 2049162, 692749, + 762955, 118916, 1119413, 274739, 470862, 292880, 414786, 1218416, 1709535, 279622, + 1285940, 469442, 1987188, 675485, 740244, 1341026, 1852022, 59670, 1259557, 284173, + 2062010, 483778, 1897192, 1651813, 1718729, 388128, 957145, 1285996, 1831671, + 497806, 1832924, 559068, 784092, 170059, 1212079, 237095, 734694, 767545, 871328, + 1197522, 1369692, 221296, 1792535, 907705, 1233817, 571488, 2033222, 636603, + 1104823, 354658, 1213051, 1424476, 1684826, 643531, 1615184, 659947, 1061345, + 412620, 605072, 904987, 1590212, 1558823, 1587538, 1785968, 2088976, 452351, + 1338442, 460036, 616323, 857148, 1950194, 1087766, 1868668, 558337, 1358354, + 636084, 788194, 566027, 675396, 1825562, 2095311, 63285, 1910522, 709285, 1519032, + 167798, 474629, 225257, 1897035, 155141, 2043903, 959984, 1559487, 698805, 1800434, + 1062227, 1271147, 144322, 1580619, 1386291, 1521451, 852563, 957301, 1446230, + 1523630, 364335, 2071342, 1235960, 1310200, 449297, 601907, 634608, 2014188, 81019, + 839298, 474427, 1831684, 210231, 1370197, 1261151, 1816924, 329886, 699000, 372746, + 1891547, 671945, 1301809, 873541, 1034843, 377922, 680790, 1280670, 1510355, + 771245, 1966090, 1483940, 2001054, 426637, 1257587, 815969, 1277447, 631165, + 722596, 1283260, 1948358, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 4069, 463738, 457648, 1094395, 353041, 845577, 1338264, 1960371, 269620, 856637, + 560213, 877722, 450359, 826410, 714399, 1983540, 126148, 1080255, 1138998, 1761600, + 427345, 1932802, 881882, 2068285, 447232, 829316, 903027, 1193156, 541454, 1521796, + 1078148, 1889459, 124993, 1347183, 581805, 1443350, 179226, 1577228, 651270, 1325494, + 683884, 1089322, 793100, 1463641, 1237884, 1941965, 1796434, 1863807, 230979, 828024, + 1428386, 1491953, 711293, 1450044, 951912, 1655589, 756010, 1309938, 1041860, 1350970, + 814780, 1539264, 1035122, 1455759, 6420, 774570, 1625325, 1729420, 753562, 1103469, + 829724, 1258289, 568974, 756682, 583406, 738962, 570441, 658047, 1879798, 1920132, + 288029, 716348, 771535, 1896895, 750194, 1355985, 1833382, 1937010, 445480, 519375, + 1044543, 1447869, 684723, 1795442, 760755, 1087503, 27203, 401767, 515774, 2096530, + 220510, 1531978, 682810, 1202695, 102739, 159328, 249040, 1376984, 714328, 1532836, + 1849127, 1900521, 64133, 118448, 1010819, 1789329, 1399572, 1772831, 1485143, 2060346, + 128615, 1309550, 1573721, 1746738, 757879, 1130173, 793709, 1847068, 44228, 228896, + 412976, 1276115, 293492, 737005, 1674045, 1944525, 331939, 1510392, 369242, 1077155, + 426460, 1673372, 431032, 1274877, 60341, 294808, 695707, 2027632, 240042, 1252879, + 1725057, 1913005, 313836, 526303, 515786, 1468016, 545971, 1193874, 875617, 1497527, + 112951, 733634, 1406066, 1866156, 476895, 1582936, 1613761, 1810957, 555593, 1637664, + 1205103, 2010551, 801136, 1350946, 1416652, 1881050, 251209, 1831040, 485897, 1321291, + 526290, 759360, 833919, 1197480, 275949, 437926, 1352279, 1907841, 1174293, 1232730, + 1591652, 1724868, 64129, 638812, 164647, 1927780, 226966, 1708268, 725951, 1398329, + 404993, 1261809, 933394, 1045040, 448233, 673005, 481885, 1180107, 135281, 1803975, + 1109497, 1803585, 532625, 1135255, 1119381, 1966487, 557305, 2051233, 615421, 1411567, + 610639, 1787653, 1241629, 1372160, 127977, 1274377, 556987, 822140, 886894, 1502085, + 1317350, 1334492, 221984, 990436, 1138067, 1488903, 525849, 1181021, 1763583, 1797031, + 391839, 736791, 1471289, 1549100, 483303, 750308, 1537541, 1566177, 659853, 1733147, + 864458, 1431423, 1528133, 1779129, 1945881, 2012012, 7510, 2047486, 1071115, 1190891, + 209477, 1579776, 452538, 1743654, 61983, 1504258, 1687339, 2091843, 336350, 524705, + 1133333, 1162168, 20622, 107880, 657023, 1441283, 352745, 1959673, 1253812, 1948787, + 57245, 2008612, 128314, 429920, 106537, 2057002, 2060106, 2090515, 15120, 1229368, + 1691769, 1924728, 90639, 562555, 626612, 1794414, 218678, 1909493, 980759, 1591385, + 714695, 1440327, 1320404, 1828751, 84296, 1186698, 440022, 1235700, 576284, 1694165, + 1568289, 2002405, 956662, 1163003, 1235075, 1791789, 1364965, 1669818, 1464088, + 1574228, 47568, 911259, 975879, 1880881, 98599, 303565, 1387907, 1576200, 92625, + 1959308, 333801, 1231633, 772862, 1284483, 1534241, 2013255, 74617, 1025792, 1135731, + 1451877, 473457, 1073612, 1045423, 1746842, 494445, 1703635, 1306798, 2003521, 949685, + 1927757, 1496880, 1857562, 161571, 1044916, 640458, 1053372, 719774, 1292407, 1131831, + 1741201, 374920, 1893201, 915430, 2028608, 679984, 1165018, 1065417, 2046590, 464822, + 1353906, 810126, 1433305, 882328, 1630701, 1519924, 1596363, 560399, 2011590, 1306858, + 1629806, 1287457, 1615912, 1712644, 1924703, 7943, 1336360, 1610242, 1822737, 474625, + 2088181, 491226, 2067675, 200602, 1281328, 1025938, 1898799, 697976, 1208680, 863807, + 1728279, 235165, 1134723, 1062949, 1834188, 1078943, 1832179, 1383472, 2066945, 278003, + 1551243, 328896, 1274678, 329449, 894853, 1807715, 2007736, 37987, 878450, 1503193, + 1885025, 82368, 1588984, 205464, 1958945, 352190, 581519, 977218, 1948110, 605118, + 1616091, 1170257, 1979591, 350625, 935037, 1171330, 1579630, 503298, 1825558, 667693, + 1930990, 406035, 475116, 840775, 1489431, 669255, 1365102, 1615968, 1714470, 37008, + 1765323, 694838, 1838285, 1254886, 1713979, 1376019, 1683443, 189227, 2040007, 212830, + 1846721, 328541, 675639, 1117953, 1173356, 174554, 1453766, 1016781, 1380416, 284565, + 916510, 770336, 1613349, 313668, 622218, 1284478, 1831739, 404140, 728071, 834045, + 2069311, 51289, 1600412, 412477, 2062451, 130962, 2053156, 465929, 709254, 117855, + 1392948, 136673, 374064, 121591, 1688669, 846029, 994804, 675059, 1448777, 1045103, + 1240057, 816462, 1499323, 1890697, 1896908, 826192, 959241, 833980, 1301853, 1149691, + 1227108, 1164702, 1520364, + ]], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [0; 32], + solutions: &[ + &[ + 592534, 16727887, 7453057, 25925862, 3112444, 22940957, 11281555, 31775301, + 1334223, 20443726, 11070438, 27290152, 4163350, 8213747, 9315696, 19739115, + 1204738, 23545872, 1776094, 13506389, 6697536, 27749507, 11388567, 14622750, + 4026870, 14622947, 8538779, 27133048, 11652285, 21221152, 22429643, 26529065, + ], + &[ + 893099, 8838806, 28398733, 31357275, 16596368, 25123776, 18326148, 31682454, + 924167, 27761424, 20546064, 30880786, 2931034, 11343701, 17011529, 26876917, + 4915668, 8097132, 17630254, 19828133, 5205703, 15014329, 13248799, 31182371, + 7887075, 22909946, 28758238, 31473391, 14791659, 33348545, 23436578, 26267836, + ], + &[ + 1948823, 6927010, 5051182, 16853572, 7567151, 8174004, 9697548, 30082354, 4126978, + 11219458, 12046931, 26296269, 6960990, 7513282, 15641819, 29553952, 2075509, + 3917596, 26506206, 32423116, 20172796, 26046597, 21008102, 32605968, 7733247, + 28951159, 19401173, 22240259, 12527355, 25816053, 26924563, 30167801, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 370176, 12908777, 1114179, 27164301, 6258700, 25604518, 23849263, 25098550, + 3874837, 22260519, 6421829, 20746376, 9010370, 14958301, 11701370, 20286183, + 3453033, 22917202, 17399732, 25201320, 12365907, 25599116, 12861876, 16581537, + 6291684, 17504753, 17494629, 17928408, 10119629, 10615318, 17868827, 20213583, + ], + &[ + 582263, 3783052, 2313999, 19478261, 1954747, 14513744, 5696384, 9983371, 770696, + 25399708, 2469656, 31060031, 1409486, 25011708, 6197016, 24800042, 4526208, + 20923264, 22532911, 24458988, 19054856, 19620962, 21223763, 25258694, 5339436, + 15681349, 11143785, 21451088, 8434833, 30577236, 27811311, 32663733, + ], + &[ + 635733, 25222820, 21014930, 29574076, 1000985, 5604521, 6974734, 19935829, 3041402, + 6498908, 27180330, 29522758, 3065872, 28403257, 5814381, 33337207, 1920304, + 16178841, 24948948, 25474220, 14568607, 30131615, 16282584, 28097350, 6277286, + 20609353, 13688741, 20448955, 8669674, 28133172, 18969419, 33014245, + ], + &[ + 1784595, 8730569, 2952232, 8311088, 3848398, 24535350, 6741302, 15864803, 5653320, + 16018355, 17835034, 29486303, 5823367, 20140719, 7233264, 33483182, 3117353, + 20053611, 3338894, 15846604, 7165521, 28162236, 8412349, 11018248, 7341551, + 18365873, 16351743, 22192468, 8662075, 9732645, 14238971, 22027130, + ], + &[ + 1791926, 8318711, 26251202, 32356717, 6997365, 25735638, 21576954, 30111878, + 3898334, 19905391, 5033991, 16030336, 5245813, 26522082, 5669465, 16635645, + 8277609, 22422842, 13069153, 29511907, 20365907, 23921315, 24326546, 32342867, + 12058103, 23466254, 19021516, 19329156, 15273796, 15658582, 15782868, 30953403, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 631838, 32379030, 12115828, 15370934, 1071098, 28542374, 3749356, 23094728, + 1030877, 4102154, 3296262, 16677836, 7373429, 23553272, 13706818, 22718294, + 1600870, 2009968, 24236940, 26722391, 3296672, 30961726, 8361013, 20154770, + 3094572, 28709268, 6668495, 32281682, 11480232, 24080407, 21721486, 26351116, + ], + &[ + 2024468, 30788885, 16549044, 31105157, 10766172, 27803398, 14188383, 18350597, + 8340166, 12112117, 9771703, 16475394, 15638163, 19852515, 16164133, 21283881, + 3012382, 10164383, 4371003, 27267590, 4579840, 32997246, 17142413, 27563106, + 4959833, 19397820, 7489484, 26132602, 7957443, 27721944, 26669199, 27861139, + ], + &[ + 5378620, 10759970, 17807788, 29226493, 11529006, 22674062, 17704747, 23436136, + 10872275, 25829134, 15459988, 21678082, 17603136, 22657822, 22774669, 23569569, + 6492083, 20372131, 27398382, 33053456, 21986403, 23346432, 29327458, 33052852, + 13637567, 26765408, 26834306, 29589598, 17363888, 31088383, 17860587, 20580709, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 911662, 22138389, 8210265, 31274530, 8029780, 20878462, 18256796, 24246891, 1935509, + 22500647, 18385714, 21573406, 5314654, 11279996, 7578895, 12470048, 3573289, 16335834, + 8230824, 25830081, 4414521, 7228234, 20359437, 21115537, 11102213, 12353678, 19277545, + 26893604, 13111251, 30773720, 14408826, 26047501, + ]], + }, +]; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index b77debca35..933945cffa 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -9,9 +9,9 @@ use std::io::Cursor; use std::mem::size_of; #[derive(Clone, Copy)] -struct Params { - n: u32, - k: u32, +pub(crate) struct Params { + pub(crate) n: u32, + pub(crate) k: u32, } #[derive(Clone)] @@ -125,8 +125,8 @@ impl fmt::Display for Error { impl std::error::Error for Error {} -#[derive(Debug)] -enum Kind { +#[derive(Debug, PartialEq)] +pub(crate) enum Kind { InvalidParams, Collision, OutOfOrder, @@ -356,171 +356,33 @@ pub fn is_valid_solution( mod tests { use super::is_valid_solution_iterative; use super::is_valid_solution_recursive; - use super::{Error, Params}; - - fn is_valid_solution( - n: u32, - k: u32, - input: &[u8], - nonce: &[u8], - indices: &[u32], - ) -> Result<(), Error> { - let p = Params::new(n, k).unwrap(); - is_valid_solution_iterative(p, input, nonce, indices)?; - is_valid_solution_recursive(p, input, nonce, indices)?; + use crate::test_vectors::{INVALID_TEST_VECTORS, VALID_TEST_VECTORS}; - Ok(()) + #[test] + fn valid_test_vectors() { + for tv in VALID_TEST_VECTORS { + for soln in tv.solutions { + is_valid_solution_iterative(tv.params, tv.input, &tv.nonce, soln).unwrap(); + is_valid_solution_recursive(tv.params, tv.input, &tv.nonce, soln).unwrap(); + } + } } #[test] - fn equihash_test_cases() { - let input = b"block header"; - let mut nonce = [0 as u8; 32]; - let mut indices = vec![ - 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, 129167, - 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, - 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, - ]; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); - - indices = vec![ - 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, - 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, - 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, - ]; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); - - indices = vec![ - 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, - 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, - 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, - 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, - 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, - 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, - 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, - 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, - 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, - 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, - 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, - 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, - 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, - 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, - 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, - 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, - 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, - 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, - 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, - 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, - 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, - 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, - 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, - 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, - 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, - 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, - 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, - 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, - 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, - 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, - 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, - 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, - 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, - 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, - 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, - 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, - 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, - 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, - 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, - 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, - 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, - 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, - 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, - 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, - 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, - 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, - 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, - 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, - 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, - 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, - 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, - 1971986, - ]; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); - is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); - - nonce[0] = 1; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); - is_valid_solution(200, 9, input, &nonce, &indices).unwrap_err(); - - indices = vec![ - 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, - 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, - 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, - ]; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap(); - - indices = vec![ - 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, - 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, - 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, 1068055, - 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, 1117445, 1936058, - 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, 21678, 822781, - 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, 934427, 1068834, - 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, 1865216, 45723, 1820952, - 1160970, 1585983, 422549, 1973097, 1296271, 2006382, 650084, 809838, 871727, 1080419, - 28500, 1471829, 384406, 619459, 212041, 1466258, 481435, 866461, 145340, 1403843, - 1339592, 1405761, 163425, 1073771, 285027, 1488210, 167744, 1182267, 1354059, 2089602, - 921700, 2059931, 1704721, 1853088, 585171, 739246, 747551, 1520527, 590255, 1175747, - 705292, 998433, 522014, 1931179, 1629531, 1692879, 588830, 1799457, 963672, 1664237, - 775408, 1926741, 907030, 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, - 88704, 1302687, 579526, 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, - 1521894, 1917599, 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, - 1316156, 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, - 1565477, 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, - 1144365, 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, - 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, 962900, - 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, 862432, - 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, 54984, - 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, 1403350, - 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, 456321, - 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, 1156906, - 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, 139460, - 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, 1514260, - 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, 1972064, 254647, - 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, 1768467, 853790, - 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, 920754, 1507358, 12883, - 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, 18523, 1236125, 669105, - 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, 985912, 2091029, 84065, - 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, 288960, 861994, 622074, - 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, 851053, 1715626, 531385, - 1213667, 1093995, 1863757, 630365, 1851894, 1328101, 1770446, 31900, 734027, 1078651, - 1701535, 123276, 1916343, 581822, 1681706, 573135, 818091, 1454710, 2052521, 1150284, - 1451159, 1482280, 1811430, 26321, 785837, 877980, 2073103, 107324, 727248, 1785460, - 1840517, 184560, 185640, 364103, 1878753, 518459, 1984029, 964109, 1884200, 74003, - 527272, 516232, 711247, 148582, 209254, 634610, 1534140, 376714, 1573267, 421225, - 1265101, 1078858, 1374310, 1806283, 2091298, 23392, 389637, 413663, 1066737, 226164, - 762552, 1048220, 1583397, 40092, 277435, 775449, 1533894, 202582, 390703, 346741, - 1027320, 523034, 809424, 584882, 1296934, 528062, 733331, 1212771, 1958651, 653372, - 1313962, 1366332, 1784489, 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, - 1698486, 57289, 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, - 1285209, 265623, 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, - 1296171, 158847, 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, - 133251, 1136222, 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, - 1457135, 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, - 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, 1137531, - 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, 1939366, 232907, - 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, 229793, 1576982, 1420059, - 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, - 1822645, 1082368, 1392894, - ]; - is_valid_solution(96, 5, input, &nonce, &indices).unwrap_err(); - is_valid_solution(200, 9, input, &nonce, &indices).unwrap(); - - let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; - indices = vec![ - 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, - 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, - 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, - ]; - is_valid_solution(96, 5, input2, &nonce, &indices).unwrap(); + fn invalid_test_vectors() { + for tv in INVALID_TEST_VECTORS { + assert_eq!( + is_valid_solution_iterative(tv.params, tv.input, &tv.nonce, &tv.solution) + .unwrap_err() + .0, + tv.error + ); + assert_eq!( + is_valid_solution_recursive(tv.params, tv.input, &tv.nonce, &tv.solution) + .unwrap_err() + .0, + tv.error + ); + } } } From c89d1c847030c933d8f78df722e5f0f3d9e569d3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 20:52:47 +1200 Subject: [PATCH 099/210] equihash: Test that all bits of the solution matter Migrated from src/test/equihash_tests.cpp --- components/equihash/src/verify.rs | 32 +++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 933945cffa..9b1e951a2c 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -354,8 +354,7 @@ pub fn is_valid_solution( #[cfg(test)] mod tests { - use super::is_valid_solution_iterative; - use super::is_valid_solution_recursive; + use super::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; use crate::test_vectors::{INVALID_TEST_VECTORS, VALID_TEST_VECTORS}; #[test] @@ -385,4 +384,33 @@ mod tests { ); } } + + #[test] + fn all_bits_matter() { + // Initialize the state according to one of the valid test vectors. + let n = 96; + let k = 5; + let input = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; + let nonce = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + let soln = &[ + 0x04, 0x6a, 0x8e, 0xd4, 0x51, 0xa2, 0x19, 0x73, 0x32, 0xe7, 0x1f, 0x39, 0xdb, 0x9c, + 0x79, 0xfb, 0xf9, 0x3f, 0xc1, 0x44, 0x3d, 0xa5, 0x8f, 0xb3, 0x8d, 0x05, 0x99, 0x17, + 0x21, 0x16, 0xd5, 0x55, 0xb1, 0xb2, 0x1f, 0x32, 0x70, 0x5c, 0xe9, 0x98, 0xf6, 0x0d, + 0xa8, 0x52, 0xf7, 0x7f, 0x0e, 0x7f, 0x4d, 0x63, 0xfc, 0x2d, 0xd2, 0x30, 0xa3, 0xd9, + 0x99, 0x53, 0xa0, 0x78, 0x7d, 0xfe, 0xfc, 0xab, 0x34, 0x1b, 0xde, 0xc8, + ]; + + // Prove that the solution is valid. + is_valid_solution(n, k, input, &nonce, soln).unwrap(); + + // Changing any single bit of the encoded solution should make it invalid. + for i in 0..soln.len() * 8 { + let mut mutated = soln.to_vec(); + mutated[i / 8] ^= 1 << (i % 8); + is_valid_solution(n, k, input, &nonce, &mutated).unwrap_err(); + } + } } From 20bb9a6c0f73cf352431665b53ced3cdb8a4ab84 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 21:12:39 +1200 Subject: [PATCH 100/210] equihash: Test vectors for expand_array and indices_from_minimal Migrated from src/gtest/test_equihash.cpp --- components/equihash/src/verify.rs | 115 +++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 9b1e951a2c..939b483782 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -354,9 +354,122 @@ pub fn is_valid_solution( #[cfg(test)] mod tests { - use super::{is_valid_solution, is_valid_solution_iterative, is_valid_solution_recursive}; + use super::{ + expand_array, indices_from_minimal, is_valid_solution, is_valid_solution_iterative, + is_valid_solution_recursive, Params, + }; use crate::test_vectors::{INVALID_TEST_VECTORS, VALID_TEST_VECTORS}; + #[test] + fn array_expansion() { + let check_array = |(bit_len, byte_pad), compact, expanded| { + assert_eq!(expand_array(compact, bit_len, byte_pad), expanded); + }; + + // 8 11-bit chunks, all-ones + check_array( + (11, 0), + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, + 0x07, 0xff, + ][..], + ); + // 8 21-bit chunks, alternating 1s and 0s + check_array( + (21, 0), + &[ + 0xaa, 0xaa, 0xad, 0x55, 0x55, 0x6a, 0xaa, 0xab, 0x55, 0x55, 0x5a, 0xaa, 0xaa, 0xd5, + 0x55, 0x56, 0xaa, 0xaa, 0xb5, 0x55, 0x55, + ], + &[ + 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, + 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, + ][..], + ); + // 8 21-bit chunks, based on example in the spec + check_array( + (21, 0), + &[ + 0x00, 0x02, 0x20, 0x00, 0x0a, 0x7f, 0xff, 0xfe, 0x00, 0x12, 0x30, 0x22, 0xb3, 0x82, + 0x26, 0xac, 0x19, 0xbd, 0xf2, 0x34, 0x56, + ], + &[ + 0x00, 0x00, 0x44, 0x00, 0x00, 0x29, 0x1f, 0xff, 0xff, 0x00, 0x01, 0x23, 0x00, 0x45, + 0x67, 0x00, 0x89, 0xab, 0x00, 0xcd, 0xef, 0x12, 0x34, 0x56, + ][..], + ); + // 16 14-bit chunks, alternating 11s and 00s + check_array( + (14, 0), + &[ + 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, + 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, + ], + &[ + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, + ][..], + ); + // 8 11-bit chunks, all-ones, 2-byte padding + check_array( + (11, 2), + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, + 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, + 0x00, 0x00, 0x07, 0xff, + ][..], + ); + } + + #[test] + fn minimal_solution_repr() { + let check_repr = |minimal, indices| { + assert_eq!( + indices_from_minimal(Params { n: 80, k: 3 }, minimal).unwrap(), + indices, + ); + }; + + // The solutions here are not intended to be valid. + check_repr( + &[ + 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x80, + 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x01, + ], + &[1, 1, 1, 1, 1, 1, 1, 1], + ); + check_repr( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, + ], + ); + check_repr( + &[ + 0x0f, 0xff, 0xf8, 0x00, 0x20, 0x03, 0xff, 0xfe, 0x00, 0x08, 0x00, 0xff, 0xff, 0x80, + 0x02, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x80, + ], + &[131071, 128, 131071, 128, 131071, 128, 131071, 128], + ); + check_repr( + &[ + 0x00, 0x02, 0x20, 0x00, 0x0a, 0x7f, 0xff, 0xfe, 0x00, 0x4d, 0x10, 0x01, 0x4c, 0x80, + 0x0f, 0xfc, 0x00, 0x00, 0x2f, 0xff, 0xff, + ], + &[68, 41, 2097151, 1233, 665, 1023, 1, 1048575], + ); + } + #[test] fn valid_test_vectors() { for tv in VALID_TEST_VECTORS { From c921cfcf9738a9a0335631ad683d550a9d3cecf7 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 10 Jul 2020 22:15:23 +1200 Subject: [PATCH 101/210] zcash_proofs: Add LocalProver::bundled Requires the bundled-prover feature, which enables the wagyu-zcash-parameters crate and adds around 50 MiB to the overall binary size. That crate bundles the same Sapling parameter files we normally obtain from disk, so we constrain them to match the same hard-coded hashes. --- zcash_proofs/Cargo.toml | 2 ++ zcash_proofs/src/lib.rs | 30 ++++++++++++++++++++++++------ zcash_proofs/src/prover.rs | 33 ++++++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 4a58e9dfbe..1f0ded25f7 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -20,6 +20,7 @@ ff = { version = "0.6", path = "../ff" } minreq = { version = "2", features = ["https"], optional = true } pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" +wagyu-zcash-parameters = { version = "0.2", optional = true } zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [dev-dependencies] @@ -27,6 +28,7 @@ rand_xorshift = "0.2" [features] default = ["local-prover", "multicore"] +bundled-prover = ["wagyu-zcash-parameters"] download-params = ["minreq"] local-prover = ["directories"] multicore = ["bellman/multicore"] diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index e14a269530..7c574c2955 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -22,11 +22,13 @@ mod hashreader; pub mod sapling; pub mod sprout; -#[cfg(feature = "local-prover")] +#[cfg(any(feature = "local-prover", feature = "bundled-prover"))] pub mod prover; // Circuit names +#[cfg(feature = "local-prover")] const SAPLING_SPEND_NAME: &str = "sapling-spend.params"; +#[cfg(feature = "local-prover")] const SAPLING_OUTPUT_NAME: &str = "sapling-output.params"; // Circuit hashes @@ -118,11 +120,27 @@ pub fn load_parameters( let sprout_fs = sprout_path.map(|p| File::open(p).expect("couldn't load Sprout groth16 parameters file")); - let mut spend_fs = hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, spend_fs)); - let mut output_fs = - hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, output_fs)); - let mut sprout_fs = - sprout_fs.map(|fs| hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, fs))); + parse_parameters( + BufReader::with_capacity(1024 * 1024, spend_fs), + BufReader::with_capacity(1024 * 1024, output_fs), + sprout_fs.map(|fs| BufReader::with_capacity(1024 * 1024, fs)), + ) +} + +fn parse_parameters( + spend_fs: R, + output_fs: R, + sprout_fs: Option, +) -> ( + Parameters, + PreparedVerifyingKey, + Parameters, + PreparedVerifyingKey, + Option>, +) { + let mut spend_fs = hashreader::HashReader::new(spend_fs); + let mut output_fs = hashreader::HashReader::new(output_fs); + let mut sprout_fs = sprout_fs.map(|fs| hashreader::HashReader::new(fs)); // Deserialize params let spend_params = Parameters::::read(&mut spend_fs, false) diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index d1c23aac7a..5201a7e074 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -2,7 +2,6 @@ use bellman::groth16::{Parameters, PreparedVerifyingKey}; use pairing::bls12_381::{Bls12, Fr}; -use std::path::Path; use zcash_primitives::{ jubjub::{edwards, fs::Fs, Unknown}, primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, @@ -16,10 +15,15 @@ use zcash_primitives::{ JUBJUB, }; -use crate::{ - default_params_folder, load_parameters, sapling::SaplingProvingContext, SAPLING_OUTPUT_NAME, - SAPLING_SPEND_NAME, -}; +use crate::sapling::SaplingProvingContext; + +#[cfg(feature = "local-prover")] +use crate::{default_params_folder, load_parameters, SAPLING_OUTPUT_NAME, SAPLING_SPEND_NAME}; +#[cfg(feature = "local-prover")] +use std::path::Path; + +#[cfg(feature = "bundled-prover")] +use crate::parse_parameters; /// An implementation of [`TxProver`] using Sapling Spend and Output parameters from /// locally-accessible paths. @@ -48,6 +52,7 @@ impl LocalTxProver { /// /// This function will panic if the paths do not point to valid parameter files with /// the expected hashes. + #[cfg(feature = "local-prover")] pub fn new(spend_path: &Path, output_path: &Path) -> Self { let (spend_params, spend_vk, output_params, _, _) = load_parameters(spend_path, output_path, None); @@ -79,6 +84,7 @@ impl LocalTxProver { /// /// This function will panic if the parameters in the default local location do not /// have the expected hashes. + #[cfg(feature = "local-prover")] pub fn with_default_location() -> Option { let params_dir = default_params_folder()?; let (spend_path, output_path) = if params_dir.exists() { @@ -95,6 +101,23 @@ impl LocalTxProver { Some(LocalTxProver::new(&spend_path, &output_path)) } + + /// Creates a `LocalTxProver` using Sapling parameters bundled inside the binary. + /// + /// This requires the `bundled-prover` feature, which will increase the binary size by + /// around 50 MiB. + #[cfg(feature = "bundled-prover")] + pub fn bundled() -> Self { + let (spend_buf, output_buf) = wagyu_zcash_parameters::load_sapling_parameters(); + let (spend_params, spend_vk, output_params, _, _) = + parse_parameters(&spend_buf[..], &output_buf[..], None); + + LocalTxProver { + spend_params, + spend_vk, + output_params, + } + } } impl TxProver for LocalTxProver { From 71d31abad66de31c81ea16824cebc3498a76fc66 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 30 Jul 2020 12:50:21 +0800 Subject: [PATCH 102/210] Set activation heights for Canopy --- zcash_primitives/src/consensus.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 1b730148d4..f0ea1003c8 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -26,7 +26,7 @@ impl Parameters for MainNetwork { NetworkUpgrade::Sapling => Some(419_200), NetworkUpgrade::Blossom => Some(653_600), NetworkUpgrade::Heartwood => Some(903_000), - NetworkUpgrade::Canopy => None, + NetworkUpgrade::Canopy => Some(1_046_400), } } } @@ -42,7 +42,7 @@ impl Parameters for TestNetwork { NetworkUpgrade::Sapling => Some(280_000), NetworkUpgrade::Blossom => Some(584_000), NetworkUpgrade::Heartwood => Some(903_800), - NetworkUpgrade::Canopy => None, + NetworkUpgrade::Canopy => Some(1_028_500), } } } @@ -243,8 +243,16 @@ mod tests { BranchId::Sapling, ); assert_eq!( - BranchId::for_height::(5_000_000), + BranchId::for_height::(903_000), BranchId::Heartwood, ); + assert_eq!( + BranchId::for_height::(1_046_400), + BranchId::Canopy, + ); + assert_eq!( + BranchId::for_height::(5_000_000), + BranchId::Canopy, + ); } } From b537f0f7124938bc2f4867894dd616789fae1600 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 30 Jul 2020 13:13:59 +0800 Subject: [PATCH 103/210] Pass height to methods which encrypt or decrypt Sapling outputs --- zcash_client_backend/src/decrypt.rs | 42 +++++++++++------- zcash_client_backend/src/welding_rig.rs | 47 +++++++++++++++++---- zcash_client_sqlite/src/scan.rs | 4 +- zcash_primitives/src/consensus.rs | 14 +++--- zcash_primitives/src/note_encryption.rs | 23 +++++++--- zcash_primitives/src/transaction/builder.rs | 17 +++++++- 6 files changed, 106 insertions(+), 41 deletions(-) diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index df44a2c679..ef6b94a2b9 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -1,5 +1,6 @@ use pairing::bls12_381::Bls12; use zcash_primitives::{ + consensus, note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo}, primitives::{Note, PaymentAddress}, transaction::Transaction, @@ -30,7 +31,8 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`ExtendedFullViewingKey`]s. -pub fn decrypt_transaction( +pub fn decrypt_transaction( + parameters: &P, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], ) -> Vec { @@ -49,21 +51,29 @@ pub fn decrypt_transaction( }; for (account, (ivk, ovk)) in vks.iter().enumerate() { - let ((note, to, memo), outgoing) = - match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) { - Some(ret) => (ret, false), - None => match try_sapling_output_recovery( - ovk, - &output.cv, - &output.cmu, - &epk, - &output.enc_ciphertext, - &output.out_ciphertext, - ) { - Some(ret) => (ret, true), - None => continue, - }, - }; + let ((note, to, memo), outgoing) = match try_sapling_note_decryption( + parameters, + tx.expiry_height, + ivk, + &epk, + &output.cmu, + &output.enc_ciphertext, + ) { + Some(ret) => (ret, false), + None => match try_sapling_output_recovery( + parameters, + tx.expiry_height, + ovk, + &output.cv, + &output.cmu, + &epk, + &output.enc_ciphertext, + &output.out_ciphertext, + ) { + Some(ret) => (ret, true), + None => continue, + }, + }; decrypted.push(DecryptedOutput { index, note, diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 4099e76762..e830a9169c 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -4,6 +4,7 @@ use ff::PrimeField; use std::collections::HashSet; use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_primitives::{ + consensus, jubjub::fs::Fs, merkle_tree::{CommitmentTree, IncrementalWitness}, note_encryption::try_sapling_compact_note_decryption, @@ -22,7 +23,9 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx}; /// /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented /// with this output's commitment. -fn scan_output( +fn scan_output( + parameters: &P, + height: u32, (index, output): (usize, CompactOutput), ivks: &[Fs], spent_from_accounts: &HashSet, @@ -49,10 +52,11 @@ fn scan_output( tree.append(node).unwrap(); for (account, ivk) in ivks.iter().enumerate() { - let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) { - Some(ret) => ret, - None => continue, - }; + let (note, to) = + match try_sapling_compact_note_decryption(parameters, height, ivk, &epk, &cmu, &ct) { + Some(ret) => ret, + None => continue, + }; // A note is marked as "change" if the account that received it // also spent notes in the same transaction. This will catch, @@ -83,7 +87,8 @@ fn scan_output( /// /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are /// incremented appropriately. -pub fn scan_block( +pub fn scan_block( + parameters: &P, block: CompactBlock, extfvks: &[ExtendedFullViewingKey], nullifiers: &[(&[u8], usize)], @@ -151,6 +156,8 @@ pub fn scan_block( .collect(); if let Some(output) = scan_output( + parameters, + block.height as u32, to_scan, &ivks, &spent_from_accounts, @@ -187,6 +194,7 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ + consensus, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, @@ -318,7 +326,14 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block( + &consensus::MainNetwork, + cb, + &[extfvk], + &[], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -350,7 +365,14 @@ mod tests { assert_eq!(cb.vtx.len(), 3); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block( + &consensus::MainNetwork, + cb, + &[extfvk], + &[], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -378,7 +400,14 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []); + let txs = scan_block( + &consensus::MainNetwork, + cb, + &[], + &[(&nf, account)], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 8dae82bb59..233e297500 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -9,6 +9,7 @@ use zcash_client_backend::{ proto::compact_formats::CompactBlock, welding_rig::scan_block, }; use zcash_primitives::{ + consensus, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, transaction::Transaction, @@ -188,6 +189,7 @@ pub fn scan_cached_blocks, Q: AsRef>( let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect(); scan_block( + &consensus::MainNetwork, block, &extfvks[..], &nf_refs, @@ -372,7 +374,7 @@ pub fn decrypt_and_store_transaction>( .collect::, _>, _>>()?? .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; - let outputs = decrypt_transaction(tx, &extfvks); + let outputs = decrypt_transaction(&consensus::MainNetwork, tx, &extfvks); if outputs.is_empty() { // Nothing to see here diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index f0ea1003c8..c2d42b756e 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -5,10 +5,10 @@ use std::fmt; /// Zcash consensus parameters. pub trait Parameters { - fn activation_height(nu: NetworkUpgrade) -> Option; + fn activation_height(&self, nu: NetworkUpgrade) -> Option; - fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { - match Self::activation_height(nu) { + fn is_nu_active(&self, nu: NetworkUpgrade, height: u32) -> bool { + match self.activation_height(nu) { Some(h) if h <= height => true, _ => false, } @@ -20,7 +20,7 @@ pub trait Parameters { pub struct MainNetwork; impl Parameters for MainNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { + fn activation_height(&self, nu: NetworkUpgrade) -> Option { match nu { NetworkUpgrade::Overwinter => Some(347_500), NetworkUpgrade::Sapling => Some(419_200), @@ -36,7 +36,7 @@ impl Parameters for MainNetwork { pub struct TestNetwork; impl Parameters for TestNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { + fn activation_height(&self, nu: NetworkUpgrade) -> Option { match nu { NetworkUpgrade::Overwinter => Some(207_500), NetworkUpgrade::Sapling => Some(280_000), @@ -174,9 +174,9 @@ impl BranchId { /// the given height. /// /// This is the branch ID that should be used when creating transactions. - pub fn for_height(height: u32) -> Self { + pub fn for_height(parameters: C, height: u32) -> Self { for nu in UPGRADES_IN_ORDER.iter().rev() { - if C::is_nu_active(*nu, height) { + if parameters.is_nu_active(*nu, height) { return nu.branch_id(); } } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index c05a5c887f..4238dc74ab 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,6 +1,8 @@ //! Implementation of in-band secret distribution for Zcash transactions. use crate::{ + consensus, + consensus::NetworkUpgrade, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -335,7 +337,9 @@ impl SaplingNoteEncryption { } } -fn parse_note_plaintext_without_memo( +fn parse_note_plaintext_without_memo( + parameters: &P, + height: u32, ivk: &Fs, cmu: &Fr, plaintext: &[u8], @@ -380,7 +384,9 @@ fn parse_note_plaintext_without_memo( /// `PaymentAddress` to which the note was sent. /// /// Implements section 4.17.2 of the Zcash Protocol Specification. -pub fn try_sapling_note_decryption( +pub fn try_sapling_note_decryption( + parameters: &P, + height: u32, ivk: &Fs, epk: &edwards::Point, cmu: &Fr, @@ -405,7 +411,7 @@ pub fn try_sapling_note_decryption( NOTE_PLAINTEXT_SIZE ); - let (note, to) = parse_note_plaintext_without_memo(ivk, cmu, &plaintext)?; + let (note, to) = parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext)?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -422,7 +428,9 @@ pub fn try_sapling_note_decryption( /// Implements the procedure specified in [`ZIP 307`]. /// /// [`ZIP 307`]: https://github.com/zcash/zips/pull/226 -pub fn try_sapling_compact_note_decryption( +pub fn try_sapling_compact_note_decryption( + parameters: &P, + height: u32, ivk: &Fs, epk: &edwards::Point, cmu: &Fr, @@ -438,7 +446,7 @@ pub fn try_sapling_compact_note_decryption( plaintext.copy_from_slice(&enc_ciphertext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); - parse_note_plaintext_without_memo(ivk, cmu, &plaintext) + parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext) } /// Recovery of the full note plaintext by the sender. @@ -448,7 +456,9 @@ pub fn try_sapling_compact_note_decryption( /// `PaymentAddress` to which the note was sent. /// /// Implements section 4.17.3 of the Zcash Protocol Specification. -pub fn try_sapling_output_recovery( +pub fn try_sapling_output_recovery( + parameters: &P, + height: u32, ovk: &OutgoingViewingKey, cv: &edwards::Point, cmu: &Fr, @@ -717,6 +727,7 @@ mod tests { } fn random_enc_ciphertext_with( + height: u32, ivk: Fs, mut rng: &mut R, ) -> ( diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index d554e27a6e..3cc0cb9410 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -13,6 +13,7 @@ use std::fmt; use crate::{ consensus, + consensus::NetworkUpgrade, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -86,7 +87,9 @@ pub struct SaplingOutput { } impl SaplingOutput { - pub fn new( + pub fn new( + parameters: P, + height: u32, rng: &mut R, ovk: OutgoingViewingKey, to: PaymentAddress, @@ -304,6 +307,7 @@ impl TransactionMetadata { /// Generates a [`Transaction`] from its inputs and outputs. pub struct Builder { rng: R, + height: u32, mtx: TransactionData, fee: Amount, anchor: Option, @@ -344,6 +348,7 @@ impl Builder { Builder { rng, + height, mtx, fee: DEFAULT_FEE, anchor: None, @@ -399,7 +404,15 @@ impl Builder { value: Amount, memo: Option, ) -> Result<(), Error> { - let output = SaplingOutput::new(&mut self.rng, ovk, to, value, memo)?; + let output = SaplingOutput::new( + consensus::MainNetwork, + self.height, + &mut self.rng, + ovk, + to, + value, + memo, + )?; self.mtx.value_balance -= value; From 65504d9ca784d9bc62ffbfae0dedad6945594edc Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 30 Jul 2020 12:36:12 +0800 Subject: [PATCH 104/210] Add enum Rseed to Note struct --- zcash_client_sqlite/src/scan.rs | 4 +- zcash_client_sqlite/src/transact.rs | 6 ++- zcash_primitives/src/note_encryption.rs | 60 ++++++++++++++------- zcash_primitives/src/primitives.rs | 31 ++++++++--- zcash_primitives/src/transaction/builder.rs | 28 ++++++---- zcash_proofs/src/sapling/prover.rs | 4 +- 6 files changed, 89 insertions(+), 44 deletions(-) diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 233e297500..f4510c8611 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -271,7 +271,7 @@ pub fn scan_cached_blocks, Q: AsRef>( .collect(); for output in tx.shielded_outputs { - let rcm = output.note.r.to_repr(); + let rcm = output.note.rcm().to_repr(); let nf = output.note.nf( &extfvks[output.account].fvk.vk, output.witness.position() as u64, @@ -459,7 +459,7 @@ pub fn decrypt_and_store_transaction>( ])?; } } else { - let rcm = output.note.r.to_repr(); + let rcm = output.note.rcm().to_repr(); // Try updating an existing received note. if stmt_update_received_note.execute(&[ diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index c7e9d285a1..246877ae94 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -13,7 +13,7 @@ use zcash_primitives::{ keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, note_encryption::Memo, - primitives::{Diversifier, Note}, + primitives::{Diversifier, Note, Rseed}, prover::TxProver, sapling::Node, transaction::{ @@ -249,7 +249,9 @@ pub fn create_to_address>( .vk .to_payment_address(diversifier, &JUBJUB) .unwrap(); - let note = from.create_note(note_value as u64, rcm, &JUBJUB).unwrap(); + let note = from + .create_note(note_value as u64, Rseed::BeforeZip212(rcm), &JUBJUB) + .unwrap(); let merkle_path = { let d: Vec<_> = row.get(3)?; diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 4238dc74ab..59623a9e41 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -8,7 +8,7 @@ use crate::{ fs::{Fs, FsRepr}, PrimeOrder, ToUniform, Unknown, }, - primitives::{Diversifier, Note, PaymentAddress}, + primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -233,12 +233,12 @@ fn prf_ock( /// let ovk = OutgoingViewingKey([0; 32]); /// /// let value = 1000; -/// let rcv = Fs::random(&mut rng); +/// let rcm = Fs::random(&mut rng); /// let cv = ValueCommitment:: { /// value, /// randomness: rcv.clone(), /// }; -/// let note = to.create_note(value, rcv, &JUBJUB).unwrap(); +/// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap(); /// let cmu = note.cm(&JUBJUB); /// /// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); @@ -294,12 +294,22 @@ impl SaplingNoteEncryption { // Note plaintext encoding is defined in section 5.5 of the Zcash Protocol // Specification. let mut input = [0; NOTE_PLAINTEXT_SIZE]; - input[0] = 1; + input[0] = match self.note.rseed { + Rseed::BeforeZip212(_) => 1, + Rseed::AfterZip212(_) => 2, + }; input[1..12].copy_from_slice(&self.to.diversifier().0); (&mut input[12..20]) .write_u64::(self.note.value) .unwrap(); - input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.to_repr().as_ref()); + match self.note.rseed { + Rseed::BeforeZip212(rcm) => { + input[20..COMPACT_NOTE_SIZE].copy_from_slice(rcm.to_repr().as_ref()); + } + Rseed::AfterZip212(rseed) => { + input[20..COMPACT_NOTE_SIZE].copy_from_slice(&rseed); + } + } input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; @@ -355,11 +365,15 @@ fn parse_note_plaintext_without_memo( let v = (&plaintext[12..20]).read_u64::().ok()?; - let rcm = Fs::from_repr(FsRepr( - plaintext[20..COMPACT_NOTE_SIZE] - .try_into() - .expect("slice is the correct length"), - ))?; + let mut r = [0u8; 32]; + r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); + + let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + Rseed::AfterZip212(r) + } else { + let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; + Rseed::BeforeZip212(rcm) + }; let diversifier = Diversifier(d); let pk_d = diversifier @@ -367,7 +381,7 @@ fn parse_note_plaintext_without_memo( .mul(ivk.to_repr(), &JUBJUB); let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rcm, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed, &JUBJUB).unwrap(); if note.cm(&JUBJUB) != *cmu { // Published commitment doesn't match calculated commitment @@ -517,11 +531,15 @@ pub fn try_sapling_output_recovery( let v = (&plaintext[12..20]).read_u64::().ok()?; - let rcm = Fs::from_repr(FsRepr( - plaintext[20..COMPACT_NOTE_SIZE] - .try_into() - .expect("slice is the correct length"), - ))?; + let mut r = [0u8; 32]; + r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); + + let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + Rseed::AfterZip212(r) + } else { + let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; + Rseed::BeforeZip212(rcm) + }; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -537,7 +555,7 @@ pub fn try_sapling_output_recovery( } let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rcm, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed, &JUBJUB).unwrap(); if note.cm(&JUBJUB) != *cmu { // Published commitment doesn't match calculated commitment @@ -555,7 +573,7 @@ mod tests { fs::{Fs, FsRepr}, PrimeOrder, Unknown, }, - primitives::{Diversifier, PaymentAddress, ValueCommitment}, + primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, }; use crypto_api_chachapoly::ChachaPolyIetf; use ff::{Field, PrimeField}; @@ -752,7 +770,7 @@ mod tests { let cv = value_commitment.cm(&JUBJUB).into(); let note = pa - .create_note(value, Fs::random(&mut rng), &JUBJUB) + .create_note(value, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) .unwrap(); let cmu = note.cm(&JUBJUB); @@ -1348,7 +1366,9 @@ mod tests { assert_eq!(ock.as_bytes(), tv.ock); let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); - let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap(); + let note = to + .create_note(tv.v, Rseed::BeforeZip212(rcm), &JUBJUB) + .unwrap(); assert_eq!(note.cm(&JUBJUB), cmu); // diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index cd06a60a9e..fb7a4db5f5 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -8,9 +8,11 @@ use crate::group_hash::group_hash; use crate::pedersen_hash::{pedersen_hash, Personalization}; -use byteorder::{LittleEndian, WriteBytesExt}; +use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; -use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; +use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform}; + +use crate::keys::prf_expand; use blake2s_simd::Params as Blake2sParams; @@ -207,18 +209,24 @@ impl PaymentAddress { pub fn create_note( &self, value: u64, - randomness: E::Fs, + randomness: Rseed, params: &E::Params, ) -> Option> { self.g_d(params).map(|g_d| Note { value, - r: randomness, + rseed: randomness, g_d, pk_d: self.pk_d.clone(), }) } } +#[derive(Clone, Debug)] +pub enum Rseed { + BeforeZip212(Fs), + AfterZip212([u8; 32]), +} + #[derive(Clone, Debug)] pub struct Note { /// The value of the note @@ -227,8 +235,8 @@ pub struct Note { pub g_d: edwards::Point, /// The public key of the address, g_d^ivk pub pk_d: edwards::Point, - /// The commitment randomness - pub r: E::Fs, + /// rseed + pub rseed: Rseed, } impl PartialEq for Note { @@ -236,7 +244,7 @@ impl PartialEq for Note { self.value == other.value && self.g_d == other.g_d && self.pk_d == other.pk_d - && self.r == other.r + && self.rcm() == other.rcm() } } @@ -280,7 +288,7 @@ impl Note { // Compute final commitment params .generator(FixedGenerators::NoteCommitmentRandomness) - .mul(self.r, params) + .mul(self.rcm(), params) .add(&hash_of_contents, params) } @@ -313,4 +321,11 @@ impl Note { // commitment to the x-coordinate is an injective encoding. self.cm_full_point(params).to_xy().0 } + + pub fn rcm(&self) -> E::Fs { + match self.rseed { + Rseed::BeforeZip212(rcm) => rcm, + Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x04]).as_bytes()), + } + } } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 3cc0cb9410..980aa8e34b 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -3,7 +3,7 @@ use crate::zip32::ExtendedSpendingKey; use crate::{ jubjub::fs::Fs, - primitives::{Diversifier, Note, PaymentAddress}, + primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; @@ -110,7 +110,7 @@ impl SaplingOutput { g_d, pk_d: to.pk_d().clone(), value: value.into(), - r: rcm, + rseed: Rseed::BeforeZip212(rcm), }; Ok(SaplingOutput { @@ -139,7 +139,7 @@ impl SaplingOutput { ctx, encryptor.esk().clone(), self.to, - self.note.r, + self.note.rcm(), self.note.value, ); @@ -568,7 +568,7 @@ impl Builder { &mut ctx, proof_generation_key, spend.diversifier, - spend.note.r, + spend.note.rcm(), spend.alpha, spend.note.value, anchor, @@ -628,7 +628,7 @@ impl Builder { Note { g_d, pk_d, - r: Fs::random(&mut self.rng), + rseed: Rseed::BeforeZip212(Fs::random(&mut self.rng)), value: 0, }, ) @@ -637,8 +637,13 @@ impl Builder { let esk = generate_esk(&mut self.rng); let epk = dummy_note.g_d.mul(esk, &JUBJUB); - let (zkproof, cv) = - prover.output_proof(&mut ctx, esk, dummy_to, dummy_note.r, dummy_note.value); + let (zkproof, cv) = prover.output_proof( + &mut ctx, + esk, + dummy_to, + dummy_note.rcm(), + dummy_note.value, + ); let cmu = dummy_note.cm(&JUBJUB); @@ -717,6 +722,7 @@ mod tests { consensus, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, + primitives::Rseed, prover::mock::MockTxProver, sapling::Node, transaction::components::Amount, @@ -778,7 +784,7 @@ mod tests { let mut rng = OsRng; let note1 = to - .create_note(50000, Fs::random(&mut rng), &JUBJUB) + .create_note(50000, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) .unwrap(); let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); let mut tree = CommitmentTree::new(); @@ -877,7 +883,7 @@ mod tests { } let note1 = to - .create_note(59999, Fs::random(&mut rng), &JUBJUB) + .create_note(59999, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) .unwrap(); let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); let mut tree = CommitmentTree::new(); @@ -916,7 +922,9 @@ mod tests { ); } - let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap(); + let note2 = to + .create_note(1, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) + .unwrap(); let cm2 = Node::new(note2.cm(&JUBJUB).to_repr()); tree.append(cm2).unwrap(); witness1.append(cm2).unwrap(); diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index cc3898e064..6d578030fc 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -8,7 +8,7 @@ use rand_core::OsRng; use std::ops::{AddAssign, Neg}; use zcash_primitives::{ jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown}, - primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment}, + primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, }; use zcash_primitives::{ merkle_tree::MerklePath, @@ -102,7 +102,7 @@ impl SaplingProvingContext { .g_d::(params) .expect("was a valid diversifier before"), pk_d: payment_address.pk_d().clone(), - r: rcm, + rseed: Rseed::BeforeZip212(rcm), }; let nullifier = note.nf(&viewing_key, merkle_path.position, params); From eda00ec7ad6732fdab621dfce9c804c979872bc5 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 30 Jul 2020 22:34:29 +0800 Subject: [PATCH 105/210] Pass esk to SaplingNoteEncryption::new and add generate_or_derive_esk() --- zcash_primitives/src/note_encryption.rs | 17 +++-------------- zcash_primitives/src/primitives.rs | 18 +++++++++++++++++- zcash_primitives/src/transaction/builder.rs | 6 +++--- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 59623a9e41..556e22e8b5 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -6,7 +6,7 @@ use crate::{ jubjub::{ edwards, fs::{Fs, FsRepr}, - PrimeOrder, ToUniform, Unknown, + PrimeOrder, Unknown, }, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; @@ -15,7 +15,6 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr}; -use rand_core::{CryptoRng, RngCore}; use std::convert::TryInto; use std::fmt; use std::str; @@ -135,15 +134,6 @@ impl str::FromStr for Memo { } } -pub fn generate_esk(rng: &mut R) -> Fs { - // create random 64 byte buffer - let mut buffer = [0u8; 64]; - rng.fill_bytes(&mut buffer); - - // reduce to uniform value - Fs::to_uniform(&buffer[..]) -} - /// Sapling key agreement for note encryption. /// /// Implements section 5.4.4.3 of the Zcash Protocol Specification. @@ -256,14 +246,13 @@ pub struct SaplingNoteEncryption { impl SaplingNoteEncryption { /// Creates a new encryption context for the given note. - pub fn new( + pub fn new( ovk: OutgoingViewingKey, note: Note, to: PaymentAddress, memo: Memo, - rng: &mut R, + esk: Fs, ) -> SaplingNoteEncryption { - let esk = generate_esk(rng); let epk = note.g_d.mul(esk, &JUBJUB); SaplingNoteEncryption { diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index fb7a4db5f5..79d0146496 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -8,7 +8,7 @@ use crate::group_hash::group_hash; use crate::pedersen_hash::{pedersen_hash, Personalization}; -use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; +use byteorder::{LittleEndian, WriteBytesExt}; use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform}; @@ -16,6 +16,8 @@ use crate::keys::prf_expand; use blake2s_simd::Params as Blake2sParams; +use rand_core::{CryptoRng, RngCore}; + #[derive(Clone)] pub struct ValueCommitment { pub value: u64, @@ -328,4 +330,18 @@ impl Note { Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x04]).as_bytes()), } } + + pub fn generate_or_derive_esk(&self, rng: &mut R) -> E::Fs { + match self.rseed { + Rseed::BeforeZip212(_) => { + // create random 64 byte buffer + let mut buffer = [0u8; 64]; + &rng.fill_bytes(&mut buffer); + + // reduce to uniform value + E::Fs::to_uniform(&buffer[..]) + } + Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()), + } + } } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 980aa8e34b..36062f39de 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -17,7 +17,7 @@ use crate::{ keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, - note_encryption::{generate_esk, Memo, SaplingNoteEncryption}, + note_encryption::{Memo, SaplingNoteEncryption}, prover::TxProver, redjubjub::PrivateKey, sapling::{spend_sig, Node}, @@ -132,7 +132,7 @@ impl SaplingOutput { self.note.clone(), self.to.clone(), self.memo, - rng, + self.note.generate_or_derive_esk(rng), ); let (zkproof, cv) = prover.output_proof( @@ -634,7 +634,7 @@ impl Builder { ) }; - let esk = generate_esk(&mut self.rng); + let esk = dummy_note.generate_or_derive_esk(&mut self.rng); let epk = dummy_note.g_d.mul(esk, &JUBJUB); let (zkproof, cv) = prover.output_proof( From 6904c8f933fb74542ebef9b7c889a284c84d4aa4 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 30 Jul 2020 23:07:33 +0800 Subject: [PATCH 106/210] Implement plaintext_version_is_valid() --- zcash_primitives/src/consensus.rs | 4 +++ zcash_primitives/src/note_encryption.rs | 38 +++++++++++++++++---- zcash_primitives/src/transaction/builder.rs | 10 ++++-- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index c2d42b756e..719231f023 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -13,6 +13,10 @@ pub trait Parameters { _ => false, } } + + fn zip_212_grace_period(&self) -> u32 { + 32256 + } } /// Marker struct for the production network. diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 556e22e8b5..b819acebc4 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -344,9 +344,9 @@ fn parse_note_plaintext_without_memo( plaintext: &[u8], ) -> Option<(Note, PaymentAddress)> { // Check note plaintext version - match plaintext[0] { - 0x01 => (), - _ => return None, + match plaintext_version_is_valid(parameters, height, plaintext[0]) { + true => (), + false => return None, } let mut d = [0u8; 11]; @@ -380,6 +380,32 @@ fn parse_note_plaintext_without_memo( Some((note, to)) } +pub fn plaintext_version_is_valid( + parameters: &P, + height: u32, + leadbyte: u8, +) -> bool { + if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + let grace_period_end_height = parameters + .activation_height(NetworkUpgrade::Canopy) + .expect("Should have Canopy activation height") + + parameters.zip_212_grace_period(); + + if height < grace_period_end_height && leadbyte != 0x01 && leadbyte != 0x02 { + // non-{0x01,0x02} received after Canopy activation and before grace period has elapsed + false + } else if height >= grace_period_end_height && leadbyte != 0x02 { + // non-0x02 received past (Canopy activation height + grace period) + false + } else { + true + } + } else { + // return false if non-0x01 received when Canopy is not active + leadbyte == 0x01 + } +} + /// Trial decryption of the full note plaintext by the recipient. /// /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ivk`. @@ -510,9 +536,9 @@ pub fn try_sapling_output_recovery( ); // Check note plaintext version - match plaintext[0] { - 0x01 => (), - _ => return None, + match plaintext_version_is_valid(parameters, height, plaintext[0]) { + true => (), + false => return None, } let mut d = [0u8; 11]; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 36062f39de..81198c9147 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -104,13 +104,19 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rcm = Fs::random(rng); + let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(rng)) + }; let note = Note { g_d, pk_d: to.pk_d().clone(), value: value.into(), - rseed: Rseed::BeforeZip212(rcm), + rseed, }; Ok(SaplingOutput { From 895e251793542ab05dc1a8378227ac6624ae7cb4 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 31 Jul 2020 00:44:23 +0800 Subject: [PATCH 107/210] Fix tests --- zcash_client_backend/src/welding_rig.rs | 15 +- zcash_client_sqlite/src/lib.rs | 48 +- zcash_client_sqlite/src/transact.rs | 2 + zcash_primitives/src/consensus.rs | 37 +- zcash_primitives/src/note_encryption.rs | 922 ++++++++++++++------ zcash_primitives/src/transaction/builder.rs | 2 + zcash_proofs/src/circuit/sapling.rs | 16 +- 7 files changed, 724 insertions(+), 318 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index e830a9169c..e16634bd5c 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -195,10 +195,11 @@ mod tests { use rand_core::{OsRng, RngCore}; use zcash_primitives::{ consensus, + consensus::{NetworkUpgrade, Parameters}, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, - primitives::Note, + primitives::{Note, Rseed}, transaction::components::Amount, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, @@ -257,18 +258,26 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; + let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut rng)) + }; let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), value: value.into(), - r: Fs::random(&mut rng), + rseed, }; + let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), to.clone(), Memo::default(), - &mut rng, + esk, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned(); let mut epk = vec![]; diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index e8f16de811..c0b4f7a8d7 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -100,9 +100,11 @@ mod tests { }; use zcash_primitives::{ block::BlockHash, + consensus, + consensus::{NetworkUpgrade, Parameters}, jubjub::fs::Fs, note_encryption::{Memo, SaplingNoteEncryption}, - primitives::{Note, PaymentAddress}, + primitives::{Note, PaymentAddress, Rseed}, transaction::components::Amount, zip32::ExtendedFullViewingKey, JUBJUB, @@ -120,18 +122,26 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; + let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut rng)) + }; let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), value: value.into(), - r: Fs::random(&mut rng), + rseed, }; + let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), to.clone(), Memo::default(), - &mut rng, + esk, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; @@ -168,6 +178,13 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; + let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut rng)) + }; // Create a fake CompactBlock containing the note let mut cspend = CompactSpend::new(); @@ -184,15 +201,11 @@ mod tests { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), value: value.into(), - r: Fs::random(&mut rng), + rseed, }; - let encryptor = SaplingNoteEncryption::new( - extfvk.fvk.ovk, - note.clone(), - to, - Memo::default(), - &mut rng, - ); + let esk = note.generate_or_derive_esk(&mut rng); + let encryptor = + SaplingNoteEncryption::new(extfvk.fvk.ovk, note.clone(), to, Memo::default(), esk); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; encryptor.epk().write(&mut epk).unwrap(); @@ -208,18 +221,27 @@ mod tests { // Create a fake Note for the change ctx.outputs.push({ let change_addr = extfvk.default_address().unwrap().1; + let rseed = + if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut rng)) + }; let note = Note { g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: change_addr.pk_d().clone(), value: (in_value - value).into(), - r: Fs::random(&mut rng), + rseed, }; + let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), change_addr, Memo::default(), - &mut rng, + esk, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 246877ae94..8826e2449b 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -816,6 +816,8 @@ mod tests { let output = &tx.shielded_outputs[output_index as usize]; try_sapling_output_recovery( + &consensus::MainNetwork, + SAPLING_ACTIVATION_HEIGHT as u32, &extfvk.fvk.ovk, &output.cv, &output.cmu, diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 719231f023..45dcca16a2 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -3,6 +3,12 @@ use std::convert::TryFrom; use std::fmt; +#[cfg(feature = "mainnet")] +pub const SAPLING_ACTIVATION_HEIGHT: u32 = 419_200; + +#[cfg(not(feature = "mainnet"))] +pub const SAPLING_ACTIVATION_HEIGHT: u32 = 280_000; + /// Zcash consensus parameters. pub trait Parameters { fn activation_height(&self, nu: NetworkUpgrade) -> Option; @@ -202,8 +208,8 @@ mod tests { let nu_a = UPGRADES_IN_ORDER[i - 1]; let nu_b = UPGRADES_IN_ORDER[i]; match ( - MainNetwork::activation_height(nu_a), - MainNetwork::activation_height(nu_b), + MainNetwork.activation_height(nu_a), + MainNetwork.activation_height(nu_b), ) { (Some(a), Some(b)) if a < b => (), (Some(_), None) => (), @@ -218,15 +224,9 @@ mod tests { #[test] fn nu_is_active() { - assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0)); - assert!(!MainNetwork::is_nu_active( - NetworkUpgrade::Overwinter, - 347_499 - )); - assert!(MainNetwork::is_nu_active( - NetworkUpgrade::Overwinter, - 347_500 - )); + assert!(!MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 0)); + assert!(!MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 347_499)); + assert!(MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 347_500)); } #[test] @@ -237,25 +237,28 @@ mod tests { #[test] fn branch_id_for_height() { - assert_eq!(BranchId::for_height::(0), BranchId::Sprout,); assert_eq!( - BranchId::for_height::(419_199), + BranchId::for_height::(MainNetwork, 0), + BranchId::Sprout, + ); + assert_eq!( + BranchId::for_height::(MainNetwork, 419_199), BranchId::Overwinter, ); assert_eq!( - BranchId::for_height::(419_200), + BranchId::for_height::(MainNetwork, 419_200), BranchId::Sapling, ); assert_eq!( - BranchId::for_height::(903_000), + BranchId::for_height::(MainNetwork, 903_000), BranchId::Heartwood, ); assert_eq!( - BranchId::for_height::(1_046_400), + BranchId::for_height::(MainNetwork, 1_046_400), BranchId::Canopy, ); assert_eq!( - BranchId::for_height::(5_000_000), + BranchId::for_height::(MainNetwork, 5_000_000), BranchId::Canopy, ); } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index b819acebc4..286ee28429 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -211,7 +211,7 @@ fn prf_ock( /// jubjub::fs::Fs, /// keys::OutgoingViewingKey, /// note_encryption::{Memo, SaplingNoteEncryption}, -/// primitives::{Diversifier, PaymentAddress, ValueCommitment}, +/// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, /// JUBJUB, /// }; /// @@ -226,12 +226,13 @@ fn prf_ock( /// let rcm = Fs::random(&mut rng); /// let cv = ValueCommitment:: { /// value, -/// randomness: rcv.clone(), +/// randomness: rcm.clone(), /// }; /// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap(); /// let cmu = note.cm(&JUBJUB); /// -/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); +/// let esk = note.generate_or_derive_esk(&mut rng); +/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), esk); /// let encCiphertext = enc.encrypt_note_plaintext(); /// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu); /// ``` @@ -583,6 +584,8 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { use crate::{ + consensus, + consensus::{NetworkUpgrade, Parameters, SAPLING_ACTIVATION_HEIGHT}, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -723,6 +726,7 @@ mod tests { } fn random_enc_ciphertext( + height: u32, mut rng: &mut R, ) -> ( OutgoingViewingKey, @@ -736,10 +740,20 @@ mod tests { let ivk = Fs::random(&mut rng); let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext_with(ivk, rng); + random_enc_ciphertext_with(height, ivk, rng); - assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some()); + assert!(try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ) + .is_some()); assert!(try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, &ivk, &epk, &cmu, @@ -747,6 +761,8 @@ mod tests { ) .is_some()); assert!(try_sapling_output_recovery( + &consensus::MainNetwork, + height, &ovk, &cv, &cmu, @@ -784,13 +800,20 @@ mod tests { }; let cv = value_commitment.cm(&JUBJUB).into(); - let note = pa - .create_note(value, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) - .unwrap(); + let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(rng)) + }; + + let note = pa.create_note(value, rseed, &JUBJUB).unwrap(); let cmu = note.cm(&JUBJUB); let ovk = OutgoingViewingKey([0; 32]); - let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), rng); + let esk = note.generate_or_derive_esk(&mut rng); + let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), esk); let epk = ne.epk(); let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); @@ -895,432 +918,749 @@ mod tests { #[test] fn decryption_with_invalid_ivk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption(&Fs::random(&mut rng), &epk, &cmu, &enc_ciphertext), - None - ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &Fs::random(&mut rng), + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_epk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption( - &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &cmu, - &enc_ciphertext - ), - None - ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_cmu() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &Fr::random(&mut rng), &enc_ciphertext), - None - ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &Fr::random(&mut rng), + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_tag() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, ivk, _, cmu, epk, mut enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, ivk, _, cmu, epk, mut enc_ciphertext, _) = + random_enc_ciphertext(height, &mut rng); - enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; + let leadbyte_array = [0x02, 0x03]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for (i, height_ref) in height_array.iter().enumerate() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte_array[i], + ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &Fs::random(&mut rng), - &epk, - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &Fs::random(&mut rng), + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, - &Fr::random(&mut rng), - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &Fr::random(&mut rng), + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; + let leadbyte_array = [0x02, 0x03]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for (i, height_ref) in height_array.iter().enumerate() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte_array[i], + ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn recovery_with_invalid_ovk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (mut ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (mut ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - ovk.0[0] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + ovk.0[0] ^= 0xff; + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_cv() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &edwards::Point::::rand(&mut rng, &JUBJUB), - &cmu, - &epk, - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &edwards::Point::::rand(&mut rng, &JUBJUB), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_cmu() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, _, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, cv, _, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &cv, - &Fr::random(&mut rng), - &epk, - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &Fr::random(&mut rng), + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_epk() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &cv, - &cmu, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_out_tag() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; + let leadbyte_array = [0x02, 0x03]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for (i, height_ref) in height_array.iter().enumerate() { + let height = *height_ref; + let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte_array[i], + ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_pk_d() { let mut rng = OsRng; + let height_v1 = SAPLING_ACTIVATION_HEIGHT; + let height_v2 = consensus::MainNetwork + .activation_height(NetworkUpgrade::Canopy) + .unwrap(); + let height_array = [height_v1, height_v2]; - let ivk = Fs::zero(); - let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext_with(ivk, &mut rng); + for height_ref in height_array.iter() { + let height = *height_ref; + let ivk = Fs::zero(); + let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(height, ivk, &mut rng); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + assert_eq!( + try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] @@ -1345,6 +1685,10 @@ mod tests { }; } + let height = consensus::MainNetwork + .activation_height(NetworkUpgrade::Sapling) + .expect("Should have Sapling activation height"); + for tv in test_vectors { // // Load the test vector components @@ -1391,7 +1735,14 @@ mod tests { // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption(&ivk, &epk, &cmu, &tv.c_enc) { + match try_sapling_note_decryption( + &consensus::MainNetwork, + height, + &ivk, + &epk, + &cmu, + &tv.c_enc, + ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1401,6 +1752,8 @@ mod tests { } match try_sapling_compact_note_decryption( + &consensus::MainNetwork, + height, &ivk, &epk, &cmu, @@ -1413,7 +1766,16 @@ mod tests { None => panic!("Compact note decryption failed"), } - match try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out) { + match try_sapling_output_recovery( + &consensus::MainNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &tv.c_enc, + &tv.c_out, + ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1426,7 +1788,9 @@ mod tests { // Test encryption // - let mut ne = SaplingNoteEncryption::new(ovk, note, to, Memo(tv.memo), &mut OsRng); + let _esk = note.generate_or_derive_esk(&mut OsRng); + + let mut ne = SaplingNoteEncryption::new(ovk, note, to, Memo(tv.memo), _esk); // Swap in the ephemeral keypair from the test vectors ne.esk = esk; ne.epk = epk; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 81198c9147..dbcdea046d 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -726,6 +726,7 @@ mod tests { use super::{Builder, Error}; use crate::{ consensus, + consensus::SAPLING_ACTIVATION_HEIGHT, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -760,6 +761,7 @@ mod tests { // Create a builder with 0 fee, so we can construct t outputs let mut builder = builder::Builder { rng: OsRng, + height: SAPLING_ACTIVATION_HEIGHT, mtx: TransactionData::new(), fee: Amount::zero(), anchor: None, diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 7aecb825cc..c486a8e5f7 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -544,7 +544,7 @@ fn test_input_circuit_with_bls12_381() { use zcash_primitives::{ jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, - primitives::{Diversifier, Note, ProofGenerationKey}, + primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; let params = &JubjubBls12::new(); @@ -598,7 +598,7 @@ fn test_input_circuit_with_bls12_381() { value: value_commitment.value, g_d: g_d.clone(), pk_d: payment_address.pk_d().clone(), - r: commitment_randomness.clone(), + rseed: Rseed::BeforeZip212(commitment_randomness.clone()), }; let mut position = 0u64; @@ -694,7 +694,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { use zcash_primitives::{ jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, - primitives::{Diversifier, Note, ProofGenerationKey}, + primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; let params = &JubjubBls12::new(); @@ -782,7 +782,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { value: value_commitment.value, g_d: g_d.clone(), pk_d: payment_address.pk_d().clone(), - r: commitment_randomness.clone(), + rseed: Rseed::BeforeZip212(commitment_randomness.clone()), }; let mut position = 0u64; @@ -877,7 +877,7 @@ fn test_output_circuit_with_bls12_381() { use rand_xorshift::XorShiftRng; use zcash_primitives::{ jubjub::{edwards, fs, JubjubBls12}, - primitives::{Diversifier, ProofGenerationKey}, + primitives::{Diversifier, ProofGenerationKey, Rseed}, }; let params = &JubjubBls12::new(); @@ -941,7 +941,11 @@ fn test_output_circuit_with_bls12_381() { ); let expected_cm = payment_address - .create_note(value_commitment.value, commitment_randomness, params) + .create_note( + value_commitment.value, + Rseed::BeforeZip212(commitment_randomness), + params, + ) .expect("should be valid") .cm(params); From b1ddd556af7e06fc0b0b5e844b0a8ed6ea884e41 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 31 Jul 2020 22:07:33 +0800 Subject: [PATCH 108/210] Check derived esk against claimed epk when decrypting note --- zcash_primitives/src/note_encryption.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 286ee28429..a13e8ad8f8 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -6,7 +6,7 @@ use crate::{ jubjub::{ edwards, fs::{Fs, FsRepr}, - PrimeOrder, Unknown, + PrimeOrder, ToUniform, Unknown, }, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; @@ -19,7 +19,10 @@ use std::convert::TryInto; use std::fmt; use std::str; -use crate::{keys::OutgoingViewingKey, JUBJUB}; +use crate::{ + keys::{prf_expand, OutgoingViewingKey}, + JUBJUB, +}; pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; @@ -209,7 +212,7 @@ fn prf_ock( /// use rand_core::OsRng; /// use zcash_primitives::{ /// jubjub::fs::Fs, -/// keys::OutgoingViewingKey, +/// keys::{OutgoingViewingKey, prf_expand}, /// note_encryption::{Memo, SaplingNoteEncryption}, /// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, /// JUBJUB, @@ -443,6 +446,14 @@ pub fn try_sapling_note_decryption( let (note, to) = parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext)?; + match note.rseed { + Rseed::AfterZip212(rseed) => { + let derived_esk = Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()); + assert_eq!(note.g_d.mul(derived_esk, &JUBJUB), *epk); + } + _ => (), + } + let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); From eba542c95feb990aa03543564dae15faf4050546 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 3 Aug 2020 13:39:36 +0800 Subject: [PATCH 109/210] Add activation heights as consts in consensus::Parameters --- zcash_primitives/src/consensus.rs | 28 ++- zcash_primitives/src/note_encryption.rs | 213 +++++++++----------- zcash_primitives/src/transaction/builder.rs | 8 +- 3 files changed, 120 insertions(+), 129 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 45dcca16a2..891d508809 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -3,12 +3,6 @@ use std::convert::TryFrom; use std::fmt; -#[cfg(feature = "mainnet")] -pub const SAPLING_ACTIVATION_HEIGHT: u32 = 419_200; - -#[cfg(not(feature = "mainnet"))] -pub const SAPLING_ACTIVATION_HEIGHT: u32 = 280_000; - /// Zcash consensus parameters. pub trait Parameters { fn activation_height(&self, nu: NetworkUpgrade) -> Option; @@ -20,9 +14,11 @@ pub trait Parameters { } } - fn zip_212_grace_period(&self) -> u32 { - 32256 - } + const OVERWINTER_ACTIVATION_HEIGHT: u32; + const SAPLING_ACTIVATION_HEIGHT: u32; + const BLOSSOM_ACTIVATION_HEIGHT: u32; + const HEARTWOOD_ACTIVATION_HEIGHT: u32; + const CANOPY_ACTIVATION_HEIGHT: u32; } /// Marker struct for the production network. @@ -39,6 +35,12 @@ impl Parameters for MainNetwork { NetworkUpgrade::Canopy => Some(1_046_400), } } + + const OVERWINTER_ACTIVATION_HEIGHT: u32 = 347_500; + const SAPLING_ACTIVATION_HEIGHT: u32 = 419_200; + const BLOSSOM_ACTIVATION_HEIGHT: u32 = 653_600; + const HEARTWOOD_ACTIVATION_HEIGHT: u32 = 903_000; + const CANOPY_ACTIVATION_HEIGHT: u32 = 1_046_400; } /// Marker struct for the test network. @@ -55,6 +57,12 @@ impl Parameters for TestNetwork { NetworkUpgrade::Canopy => Some(1_028_500), } } + + const OVERWINTER_ACTIVATION_HEIGHT: u32 = 207_500; + const SAPLING_ACTIVATION_HEIGHT: u32 = 280_200; + const BLOSSOM_ACTIVATION_HEIGHT: u32 = 584_000; + const HEARTWOOD_ACTIVATION_HEIGHT: u32 = 903_800; + const CANOPY_ACTIVATION_HEIGHT: u32 = 1_028_500; } /// An event that occurs at a specified height on the Zcash chain, at which point the @@ -121,6 +129,8 @@ const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ NetworkUpgrade::Canopy, ]; +pub const ZIP212_GRACE_PERIOD: u32 = 32256; + /// A globally-unique identifier for a set of consensus rules within the Zcash chain. /// /// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index a13e8ad8f8..d911225c65 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -2,7 +2,7 @@ use crate::{ consensus, - consensus::NetworkUpgrade, + consensus::{NetworkUpgrade, ZIP212_GRACE_PERIOD}, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -393,7 +393,7 @@ pub fn plaintext_version_is_valid( let grace_period_end_height = parameters .activation_height(NetworkUpgrade::Canopy) .expect("Should have Canopy activation height") - + parameters.zip_212_grace_period(); + + ZIP212_GRACE_PERIOD; if height < grace_period_end_height && leadbyte != 0x01 && leadbyte != 0x02 { // non-{0x01,0x02} received after Canopy activation and before grace period has elapsed @@ -596,7 +596,7 @@ pub fn try_sapling_output_recovery( mod tests { use crate::{ consensus, - consensus::{NetworkUpgrade, Parameters, SAPLING_ACTIVATION_HEIGHT}, + consensus::{NetworkUpgrade, Parameters, ZIP212_GRACE_PERIOD}, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -929,11 +929,10 @@ mod tests { #[test] fn decryption_with_invalid_ivk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -956,11 +955,10 @@ mod tests { #[test] fn decryption_with_invalid_epk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -983,11 +981,10 @@ mod tests { #[test] fn decryption_with_invalid_cmu() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1010,11 +1007,10 @@ mod tests { #[test] fn decryption_with_invalid_tag() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1039,11 +1035,10 @@ mod tests { #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; let leadbyte_array = [0x02, 0x03]; for (i, height_ref) in height_array.iter().enumerate() { @@ -1077,11 +1072,10 @@ mod tests { #[test] fn decryption_with_invalid_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1114,11 +1108,10 @@ mod tests { #[test] fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1151,11 +1144,10 @@ mod tests { #[test] fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1178,11 +1170,10 @@ mod tests { #[test] fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1205,11 +1196,10 @@ mod tests { #[test] fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1232,11 +1222,10 @@ mod tests { #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; let leadbyte_array = [0x02, 0x03]; for (i, height_ref) in height_array.iter().enumerate() { @@ -1270,11 +1259,10 @@ mod tests { #[test] fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1307,11 +1295,10 @@ mod tests { #[test] fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1344,11 +1331,10 @@ mod tests { #[test] fn recovery_with_invalid_ovk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1375,11 +1361,10 @@ mod tests { #[test] fn recovery_with_invalid_cv() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1405,11 +1390,10 @@ mod tests { #[test] fn recovery_with_invalid_cmu() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1435,11 +1419,10 @@ mod tests { #[test] fn recovery_with_invalid_epk() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1465,11 +1448,10 @@ mod tests { #[test] fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1496,11 +1478,10 @@ mod tests { #[test] fn recovery_with_invalid_out_tag() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1527,11 +1508,10 @@ mod tests { #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; let leadbyte_array = [0x02, 0x03]; for (i, height_ref) in height_array.iter().enumerate() { @@ -1567,11 +1547,10 @@ mod tests { #[test] fn recovery_with_invalid_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1606,11 +1585,10 @@ mod tests { #[test] fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; @@ -1646,11 +1624,10 @@ mod tests { #[test] fn recovery_with_invalid_pk_d() { let mut rng = OsRng; - let height_v1 = SAPLING_ACTIVATION_HEIGHT; - let height_v2 = consensus::MainNetwork - .activation_height(NetworkUpgrade::Canopy) - .unwrap(); - let height_array = [height_v1, height_v2]; + let height_array = [ + consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + ]; for height_ref in height_array.iter() { let height = *height_ref; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index dbcdea046d..d4fb54e1f5 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -726,7 +726,6 @@ mod tests { use super::{Builder, Error}; use crate::{ consensus, - consensus::SAPLING_ACTIVATION_HEIGHT, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -753,15 +752,20 @@ mod tests { #[test] fn binding_sig_absent_if_no_shielded_spend_or_output() { + use crate::consensus::{NetworkUpgrade, Parameters}; use crate::transaction::{ builder::{self, TransparentInputs}, TransactionData, }; + let sapling_activation_height = consensus::MainNetwork + .activation_height(NetworkUpgrade::Sapling) + .unwrap(); + // Create a builder with 0 fee, so we can construct t outputs let mut builder = builder::Builder { rng: OsRng, - height: SAPLING_ACTIVATION_HEIGHT, + height: sapling_activation_height, mtx: TransactionData::new(), fee: Amount::zero(), anchor: None, From b34e8b903cdc48c56137acaab3823918f76ed29b Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 3 Aug 2020 13:51:45 +0800 Subject: [PATCH 110/210] Add invalid version byte tests for ZIP212 --- zcash_primitives/src/note_encryption.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index d911225c65..cb7cd90449 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1036,10 +1036,11 @@ mod tests { fn decryption_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03]; + let leadbyte_array = [0x02, 0x03, 0x01]; for (i, height_ref) in height_array.iter().enumerate() { let height = *height_ref; @@ -1223,10 +1224,11 @@ mod tests { fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03]; + let leadbyte_array = [0x02, 0x03, 0x01]; for (i, height_ref) in height_array.iter().enumerate() { let height = *height_ref; @@ -1509,10 +1511,11 @@ mod tests { fn recovery_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03]; + let leadbyte_array = [0x02, 0x03, 0x01]; for (i, height_ref) in height_array.iter().enumerate() { let height = *height_ref; From 0a47a9dbea1e598a368cbd124f67fdce0f52b3ec Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 4 Aug 2020 11:23:03 +0800 Subject: [PATCH 111/210] Pass rseed to Prover --- zcash_primitives/src/primitives.rs | 2 +- zcash_primitives/src/prover.rs | 8 ++++---- zcash_primitives/src/transaction/builder.rs | 2 +- zcash_proofs/src/prover.rs | 6 +++--- zcash_proofs/src/sapling/prover.rs | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 79d0146496..4f635a867c 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -223,7 +223,7 @@ impl PaymentAddress { } } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum Rseed { BeforeZip212(Fs), AfterZip212([u8; 32]), diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 932573d991..2c40f8d562 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -2,7 +2,7 @@ use crate::{ jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, }; use pairing::bls12_381::{Bls12, Fr}; @@ -31,7 +31,7 @@ pub trait TxProver { ctx: &mut Self::SaplingProvingContext, proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, + rseed: Rseed, ar: Fs, value: u64, anchor: Fr, @@ -78,7 +78,7 @@ pub(crate) mod mock { use crate::{ jubjub::{edwards, fs::Fs, FixedGenerators, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ValueCommitment}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, }; use crate::{ @@ -104,7 +104,7 @@ pub(crate) mod mock { _ctx: &mut Self::SaplingProvingContext, proof_generation_key: ProofGenerationKey, _diversifier: Diversifier, - _rcm: Fs, + _rcm: Rseed, ar: Fs, value: u64, _anchor: Fr, diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index d4fb54e1f5..89bf449005 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -574,7 +574,7 @@ impl Builder { &mut ctx, proof_generation_key, spend.diversifier, - spend.note.rcm(), + spend.note.rseed, spend.alpha, spend.note.value, anchor, diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index d1c23aac7a..9077410afd 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -5,7 +5,7 @@ use pairing::bls12_381::{Bls12, Fr}; use std::path::Path; use zcash_primitives::{ jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, }; use zcash_primitives::{ merkle_tree::MerklePath, @@ -109,7 +109,7 @@ impl TxProver for LocalTxProver { ctx: &mut Self::SaplingProvingContext, proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, + rseed: Rseed, ar: Fs, value: u64, anchor: Fr, @@ -125,7 +125,7 @@ impl TxProver for LocalTxProver { let (proof, cv, rk) = ctx.spend_proof( proof_generation_key, diversifier, - rcm, + rseed, ar, value, anchor, diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 6d578030fc..7c55e6c874 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -43,7 +43,7 @@ impl SaplingProvingContext { &mut self, proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, + rseed: Rseed, ar: Fs, value: u64, anchor: Fr, @@ -102,7 +102,7 @@ impl SaplingProvingContext { .g_d::(params) .expect("was a valid diversifier before"), pk_d: payment_address.pk_d().clone(), - rseed: Rseed::BeforeZip212(rcm), + rseed, }; let nullifier = note.nf(&viewing_key, merkle_path.position, params); @@ -113,7 +113,7 @@ impl SaplingProvingContext { value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key), payment_address: Some(payment_address), - commitment_randomness: Some(rcm), + commitment_randomness: Some(note.rcm()), ar: Some(ar), auth_path: merkle_path .auth_path From 74b2f0a79e71e223b9f3615a536b79d6d4d346f0 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 4 Aug 2020 18:27:41 +0800 Subject: [PATCH 112/210] Pass height to decrypt_transaction() --- zcash_client_backend/src/decrypt.rs | 5 +++-- zcash_client_sqlite/src/scan.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index ef6b94a2b9..9079e9c9b1 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -32,6 +32,7 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`ExtendedFullViewingKey`]s. pub fn decrypt_transaction( + height: u32, parameters: &P, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], @@ -53,7 +54,7 @@ pub fn decrypt_transaction( for (account, (ivk, ovk)) in vks.iter().enumerate() { let ((note, to, memo), outgoing) = match try_sapling_note_decryption( parameters, - tx.expiry_height, + height, ivk, &epk, &output.cmu, @@ -62,7 +63,7 @@ pub fn decrypt_transaction( Some(ret) => (ret, false), None => match try_sapling_output_recovery( parameters, - tx.expiry_height, + height, ovk, &output.cv, &output.cmu, diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index f4510c8611..d665e4b125 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -374,7 +374,16 @@ pub fn decrypt_and_store_transaction>( .collect::, _>, _>>()?? .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; - let outputs = decrypt_transaction(&consensus::MainNetwork, tx, &extfvks); + // Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions. + let last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?; + let height = stmt_select_block.query_row(&[tx.txid().0.to_vec()], |row| { + row.get(0).or(Ok(last_height + 1)) + })?; + + let outputs = decrypt_transaction(height as u32, &consensus::MainNetwork, tx, &extfvks); if outputs.is_empty() { // Nothing to see here From a25348dfbadb9b938768a85817e70b392e9ff641 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 12:47:29 +0800 Subject: [PATCH 113/210] Revert passing Parameters to methods --- zcash_client_backend/src/decrypt.rs | 7 +- zcash_client_backend/src/lib.rs | 6 + zcash_client_backend/src/welding_rig.rs | 42 +-- zcash_client_sqlite/src/lib.rs | 28 +- zcash_client_sqlite/src/scan.rs | 8 +- zcash_client_sqlite/src/transact.rs | 9 +- zcash_primitives/src/consensus.rs | 49 ++-- zcash_primitives/src/lib.rs | 6 + zcash_primitives/src/note_encryption.rs | 267 +++++++------------- zcash_primitives/src/transaction/builder.rs | 34 +-- 10 files changed, 179 insertions(+), 277 deletions(-) diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 9079e9c9b1..1baabbf3de 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -33,7 +33,6 @@ pub struct DecryptedOutput { /// [`ExtendedFullViewingKey`]s. pub fn decrypt_transaction( height: u32, - parameters: &P, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], ) -> Vec { @@ -52,8 +51,7 @@ pub fn decrypt_transaction( }; for (account, (ivk, ovk)) in vks.iter().enumerate() { - let ((note, to, memo), outgoing) = match try_sapling_note_decryption( - parameters, + let ((note, to, memo), outgoing) = match try_sapling_note_decryption::

( height, ivk, &epk, @@ -61,8 +59,7 @@ pub fn decrypt_transaction( &output.enc_ciphertext, ) { Some(ret) => (ret, false), - None => match try_sapling_output_recovery( - parameters, + None => match try_sapling_output_recovery::

( height, ovk, &output.cv, diff --git a/zcash_client_backend/src/lib.rs b/zcash_client_backend/src/lib.rs index e3852e6acc..a4e5fb4229 100644 --- a/zcash_client_backend/src/lib.rs +++ b/zcash_client_backend/src/lib.rs @@ -15,3 +15,9 @@ pub mod wallet; pub mod welding_rig; pub use decrypt::{decrypt_transaction, DecryptedOutput}; + +#[cfg(feature = "mainnet")] +pub use zcash_primitives::consensus::MainNetwork as Network; + +#[cfg(not(feature = "mainnet"))] +pub use zcash_primitives::consensus::TestNetwork as Network; diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index e16634bd5c..f51ef3eac4 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -24,7 +24,6 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx}; /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented /// with this output's commitment. fn scan_output( - parameters: &P, height: u32, (index, output): (usize, CompactOutput), ivks: &[Fs], @@ -53,7 +52,7 @@ fn scan_output( for (account, ivk) in ivks.iter().enumerate() { let (note, to) = - match try_sapling_compact_note_decryption(parameters, height, ivk, &epk, &cmu, &ct) { + match try_sapling_compact_note_decryption::

(height, ivk, &epk, &cmu, &ct) { Some(ret) => ret, None => continue, }; @@ -88,7 +87,6 @@ fn scan_output( /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are /// incremented appropriately. pub fn scan_block( - parameters: &P, block: CompactBlock, extfvks: &[ExtendedFullViewingKey], nullifiers: &[(&[u8], usize)], @@ -155,8 +153,7 @@ pub fn scan_block( .map(|output| &mut output.witness) .collect(); - if let Some(output) = scan_output( - parameters, + if let Some(output) = scan_output::

( block.height as u32, to_scan, &ivks, @@ -194,7 +191,6 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus, consensus::{NetworkUpgrade, Parameters}, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, @@ -206,7 +202,10 @@ mod tests { }; use super::scan_block; - use crate::proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx}; + use crate::{ + proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx}, + Network, + }; fn random_compact_tx(rng: &mut R) -> CompactTx { let fake_nf = { @@ -258,7 +257,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) @@ -335,14 +334,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block( - &consensus::MainNetwork, - cb, - &[extfvk], - &[], - &mut tree, - &mut [], - ); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -374,14 +366,7 @@ mod tests { assert_eq!(cb.vtx.len(), 3); let mut tree = CommitmentTree::new(); - let txs = scan_block( - &consensus::MainNetwork, - cb, - &[extfvk], - &[], - &mut tree, - &mut [], - ); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -409,14 +394,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block( - &consensus::MainNetwork, - cb, - &[], - &[(&nf, account)], - &mut tree, - &mut [], - ); + let txs = scan_block::(cb, &[], &[(&nf, account)], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index c0b4f7a8d7..df41cf4dd3 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -33,12 +33,17 @@ use zcash_primitives::zip32::ExtendedFullViewingKey; use zcash_client_backend::constants::mainnet::{ HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, }; - #[cfg(not(feature = "mainnet"))] use zcash_client_backend::constants::testnet::{ HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, }; +#[cfg(feature = "mainnet")] +pub use zcash_primitives::consensus::MainNetwork as Network; + +#[cfg(not(feature = "mainnet"))] +pub use zcash_primitives::consensus::TestNetwork as Network; + pub mod address; pub mod chain; pub mod error; @@ -89,6 +94,7 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error: #[cfg(test)] mod tests { + use crate::Network; use ff::{Field, PrimeField}; use pairing::bls12_381::Bls12; use protobuf::Message; @@ -100,7 +106,6 @@ mod tests { }; use zcash_primitives::{ block::BlockHash, - consensus, consensus::{NetworkUpgrade, Parameters}, jubjub::fs::Fs, note_encryption::{Memo, SaplingNoteEncryption}, @@ -122,7 +127,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) @@ -178,7 +183,7 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; - let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) @@ -221,14 +226,13 @@ mod tests { // Create a fake Note for the change ctx.outputs.push({ let change_addr = extfvk.default_address().unwrap().1; - let rseed = - if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height as u32) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut rng)) - }; + let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut rng)) + }; let note = Note { g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: change_addr.pk_d().clone(), diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index d665e4b125..417d63bef4 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -9,7 +9,6 @@ use zcash_client_backend::{ proto::compact_formats::CompactBlock, welding_rig::scan_block, }; use zcash_primitives::{ - consensus, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, transaction::Transaction, @@ -19,7 +18,7 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, error::{Error, ErrorKind}, - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, + Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, }; struct CompactBlockRow { @@ -188,8 +187,7 @@ pub fn scan_cached_blocks, Q: AsRef>( let txs = { let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect(); - scan_block( - &consensus::MainNetwork, + scan_block::( block, &extfvks[..], &nf_refs, @@ -383,7 +381,7 @@ pub fn decrypt_and_store_transaction>( row.get(0).or(Ok(last_height + 1)) })?; - let outputs = decrypt_transaction(height as u32, &consensus::MainNetwork, tx, &extfvks); + let outputs = decrypt_transaction::(height as u32, tx, &extfvks); if outputs.is_empty() { // Nothing to see here diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 8826e2449b..e8c6700cf6 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -27,7 +27,7 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, error::{Error, ErrorKind}, - get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + get_target_and_anchor_heights, Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, }; /// Describes a policy for which outgoing viewing key should be able to decrypt @@ -292,7 +292,7 @@ pub fn create_to_address>( } match to { RecipientAddress::Shielded(to) => { - builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) + builder.add_sapling_output::(ovk, to.clone(), value, memo.clone()) } RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), }?; @@ -393,7 +393,7 @@ mod tests { query::{get_balance, get_verified_balance}, scan::scan_cached_blocks, tests::{fake_compact_block, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, + Network, SAPLING_ACTIVATION_HEIGHT, }; fn test_prover() -> impl TxProver { @@ -815,8 +815,7 @@ mod tests { .unwrap(); let output = &tx.shielded_outputs[output_index as usize]; - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( SAPLING_ACTIVATION_HEIGHT as u32, &extfvk.fvk.ovk, &output.cv, diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 891d508809..7020bae6e2 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -5,10 +5,10 @@ use std::fmt; /// Zcash consensus parameters. pub trait Parameters { - fn activation_height(&self, nu: NetworkUpgrade) -> Option; + fn activation_height(nu: NetworkUpgrade) -> Option; - fn is_nu_active(&self, nu: NetworkUpgrade, height: u32) -> bool { - match self.activation_height(nu) { + fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { + match Self::activation_height(nu) { Some(h) if h <= height => true, _ => false, } @@ -26,7 +26,7 @@ pub trait Parameters { pub struct MainNetwork; impl Parameters for MainNetwork { - fn activation_height(&self, nu: NetworkUpgrade) -> Option { + fn activation_height(nu: NetworkUpgrade) -> Option { match nu { NetworkUpgrade::Overwinter => Some(347_500), NetworkUpgrade::Sapling => Some(419_200), @@ -48,7 +48,7 @@ impl Parameters for MainNetwork { pub struct TestNetwork; impl Parameters for TestNetwork { - fn activation_height(&self, nu: NetworkUpgrade) -> Option { + fn activation_height(nu: NetworkUpgrade) -> Option { match nu { NetworkUpgrade::Overwinter => Some(207_500), NetworkUpgrade::Sapling => Some(280_000), @@ -194,9 +194,9 @@ impl BranchId { /// the given height. /// /// This is the branch ID that should be used when creating transactions. - pub fn for_height(parameters: C, height: u32) -> Self { + pub fn for_height(height: u32) -> Self { for nu in UPGRADES_IN_ORDER.iter().rev() { - if parameters.is_nu_active(*nu, height) { + if C::is_nu_active(*nu, height) { return nu.branch_id(); } } @@ -208,18 +208,18 @@ impl BranchId { #[cfg(test)] mod tests { + use super::{BranchId, MainNetwork, NetworkUpgrade, UPGRADES_IN_ORDER}; + use crate::consensus::Parameters; use std::convert::TryFrom; - use super::{BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; - #[test] fn nu_ordering() { for i in 1..UPGRADES_IN_ORDER.len() { let nu_a = UPGRADES_IN_ORDER[i - 1]; let nu_b = UPGRADES_IN_ORDER[i]; match ( - MainNetwork.activation_height(nu_a), - MainNetwork.activation_height(nu_b), + MainNetwork::activation_height(nu_a), + MainNetwork::activation_height(nu_b), ) { (Some(a), Some(b)) if a < b => (), (Some(_), None) => (), @@ -234,9 +234,15 @@ mod tests { #[test] fn nu_is_active() { - assert!(!MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 0)); - assert!(!MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 347_499)); - assert!(MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, 347_500)); + assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0)); + assert!(!MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_499 + )); + assert!(MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_500 + )); } #[test] @@ -247,28 +253,25 @@ mod tests { #[test] fn branch_id_for_height() { + assert_eq!(BranchId::for_height::(0), BranchId::Sprout,); assert_eq!( - BranchId::for_height::(MainNetwork, 0), - BranchId::Sprout, - ); - assert_eq!( - BranchId::for_height::(MainNetwork, 419_199), + BranchId::for_height::(419_199), BranchId::Overwinter, ); assert_eq!( - BranchId::for_height::(MainNetwork, 419_200), + BranchId::for_height::(419_200), BranchId::Sapling, ); assert_eq!( - BranchId::for_height::(MainNetwork, 903_000), + BranchId::for_height::(903_000), BranchId::Heartwood, ); assert_eq!( - BranchId::for_height::(MainNetwork, 1_046_400), + BranchId::for_height::(1_046_400), BranchId::Canopy, ); assert_eq!( - BranchId::for_height::(MainNetwork, 5_000_000), + BranchId::for_height::(5_000_000), BranchId::Canopy, ); } diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 0050bbd952..2bbc801c29 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -35,3 +35,9 @@ use crate::jubjub::JubjubBls12; lazy_static! { pub static ref JUBJUB: JubjubBls12 = JubjubBls12::new(); } + +#[cfg(feature = "mainnet")] +use crate::consensus::MainNetwork as Network; + +#[cfg(not(feature = "mainnet"))] +use crate::consensus::TestNetwork as Network; diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index cb7cd90449..cc52356c60 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -341,14 +341,13 @@ impl SaplingNoteEncryption { } fn parse_note_plaintext_without_memo( - parameters: &P, height: u32, ivk: &Fs, cmu: &Fr, plaintext: &[u8], ) -> Option<(Note, PaymentAddress)> { // Check note plaintext version - match plaintext_version_is_valid(parameters, height, plaintext[0]) { + match plaintext_version_is_valid::

(height, plaintext[0]) { true => (), false => return None, } @@ -361,7 +360,7 @@ fn parse_note_plaintext_without_memo( let mut r = [0u8; 32]; r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); - let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { Rseed::AfterZip212(r) } else { let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; @@ -384,14 +383,9 @@ fn parse_note_plaintext_without_memo( Some((note, to)) } -pub fn plaintext_version_is_valid( - parameters: &P, - height: u32, - leadbyte: u8, -) -> bool { - if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { - let grace_period_end_height = parameters - .activation_height(NetworkUpgrade::Canopy) +pub fn plaintext_version_is_valid(height: u32, leadbyte: u8) -> bool { + if P::is_nu_active(NetworkUpgrade::Canopy, height) { + let grace_period_end_height = P::activation_height(NetworkUpgrade::Canopy) .expect("Should have Canopy activation height") + ZIP212_GRACE_PERIOD; @@ -418,7 +412,6 @@ pub fn plaintext_version_is_valid( /// /// Implements section 4.17.2 of the Zcash Protocol Specification. pub fn try_sapling_note_decryption( - parameters: &P, height: u32, ivk: &Fs, epk: &edwards::Point, @@ -444,7 +437,7 @@ pub fn try_sapling_note_decryption( NOTE_PLAINTEXT_SIZE ); - let (note, to) = parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext)?; + let (note, to) = parse_note_plaintext_without_memo::

(height, ivk, cmu, &plaintext)?; match note.rseed { Rseed::AfterZip212(rseed) => { @@ -470,7 +463,6 @@ pub fn try_sapling_note_decryption( /// /// [`ZIP 307`]: https://github.com/zcash/zips/pull/226 pub fn try_sapling_compact_note_decryption( - parameters: &P, height: u32, ivk: &Fs, epk: &edwards::Point, @@ -487,7 +479,7 @@ pub fn try_sapling_compact_note_decryption( plaintext.copy_from_slice(&enc_ciphertext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); - parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext) + parse_note_plaintext_without_memo::

(height, ivk, cmu, &plaintext) } /// Recovery of the full note plaintext by the sender. @@ -498,7 +490,6 @@ pub fn try_sapling_compact_note_decryption( /// /// Implements section 4.17.3 of the Zcash Protocol Specification. pub fn try_sapling_output_recovery( - parameters: &P, height: u32, ovk: &OutgoingViewingKey, cv: &edwards::Point, @@ -548,7 +539,7 @@ pub fn try_sapling_output_recovery( ); // Check note plaintext version - match plaintext_version_is_valid(parameters, height, plaintext[0]) { + match plaintext_version_is_valid::

(height, plaintext[0]) { true => (), false => return None, } @@ -561,7 +552,7 @@ pub fn try_sapling_output_recovery( let mut r = [0u8; 32]; r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); - let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { Rseed::AfterZip212(r) } else { let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; @@ -595,7 +586,6 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { use crate::{ - consensus, consensus::{NetworkUpgrade, Parameters, ZIP212_GRACE_PERIOD}, jubjub::{ edwards, @@ -603,6 +593,7 @@ mod tests { PrimeOrder, Unknown, }, primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, + Network, }; use crypto_api_chachapoly::ChachaPolyIetf; use ff::{Field, PrimeField}; @@ -753,17 +744,11 @@ mod tests { let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, rng); - assert!(try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &enc_ciphertext - ) - .is_some()); - assert!(try_sapling_compact_note_decryption( - &consensus::MainNetwork, + assert!( + try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext) + .is_some() + ); + assert!(try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -771,8 +756,7 @@ mod tests { &enc_ciphertext[..COMPACT_NOTE_SIZE] ) .is_some()); - assert!(try_sapling_output_recovery( - &consensus::MainNetwork, + assert!(try_sapling_output_recovery::( height, &ovk, &cv, @@ -811,7 +795,7 @@ mod tests { }; let cv = value_commitment.cm(&JUBJUB).into(); - let rseed = if consensus::MainNetwork.is_nu_active(NetworkUpgrade::Canopy, height) { + let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) @@ -930,8 +914,8 @@ mod tests { fn decryption_with_invalid_ivk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -939,8 +923,7 @@ mod tests { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, + try_sapling_note_decryption::( height, &Fs::random(&mut rng), &epk, @@ -956,8 +939,8 @@ mod tests { fn decryption_with_invalid_epk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -965,8 +948,7 @@ mod tests { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, + try_sapling_note_decryption::( height, &ivk, &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), @@ -982,8 +964,8 @@ mod tests { fn decryption_with_invalid_cmu() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -991,8 +973,7 @@ mod tests { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, + try_sapling_note_decryption::( height, &ivk, &epk, @@ -1008,8 +989,8 @@ mod tests { fn decryption_with_invalid_tag() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1019,14 +1000,7 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &enc_ciphertext - ), + try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), None ); } @@ -1036,9 +1010,9 @@ mod tests { fn decryption_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + Network::CANOPY_ACTIVATION_HEIGHT - 1, + Network::CANOPY_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1057,14 +1031,7 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &enc_ciphertext - ), + try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), None ); } @@ -1074,8 +1041,8 @@ mod tests { fn decryption_with_invalid_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1093,14 +1060,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &enc_ciphertext - ), + try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), None ); } @@ -1110,8 +1070,8 @@ mod tests { fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1129,14 +1089,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &enc_ciphertext - ), + try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), None ); } @@ -1146,8 +1099,8 @@ mod tests { fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1155,8 +1108,7 @@ mod tests { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &Fs::random(&mut rng), &epk, @@ -1172,8 +1124,8 @@ mod tests { fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1181,8 +1133,7 @@ mod tests { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &ivk, &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), @@ -1198,8 +1149,8 @@ mod tests { fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1207,8 +1158,7 @@ mod tests { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1224,9 +1174,9 @@ mod tests { fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + Network::CANOPY_ACTIVATION_HEIGHT - 1, + Network::CANOPY_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1245,8 +1195,7 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1262,8 +1211,8 @@ mod tests { fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1281,8 +1230,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1298,8 +1246,8 @@ mod tests { fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1317,8 +1265,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption( - &consensus::MainNetwork, + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1334,8 +1281,8 @@ mod tests { fn recovery_with_invalid_ovk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1345,8 +1292,7 @@ mod tests { ovk.0[0] ^= 0xff; assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1364,8 +1310,8 @@ mod tests { fn recovery_with_invalid_cv() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1374,8 +1320,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &edwards::Point::::rand(&mut rng, &JUBJUB), @@ -1393,8 +1338,8 @@ mod tests { fn recovery_with_invalid_cmu() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1403,8 +1348,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1422,8 +1366,8 @@ mod tests { fn recovery_with_invalid_epk() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1432,8 +1376,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1451,8 +1394,8 @@ mod tests { fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1462,8 +1405,7 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1481,8 +1423,8 @@ mod tests { fn recovery_with_invalid_out_tag() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1492,8 +1434,7 @@ mod tests { out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1511,9 +1452,9 @@ mod tests { fn recovery_with_invalid_version_byte() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + Network::CANOPY_ACTIVATION_HEIGHT - 1, + Network::CANOPY_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1532,8 +1473,7 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1551,8 +1491,8 @@ mod tests { fn recovery_with_invalid_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1570,8 +1510,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1589,8 +1528,8 @@ mod tests { fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1609,8 +1548,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1628,8 +1566,8 @@ mod tests { fn recovery_with_invalid_pk_d() { let mut rng = OsRng; let height_array = [ - consensus::MainNetwork::SAPLING_ACTIVATION_HEIGHT, - consensus::MainNetwork::CANOPY_ACTIVATION_HEIGHT, + Network::SAPLING_ACTIVATION_HEIGHT, + Network::CANOPY_ACTIVATION_HEIGHT, ]; for height_ref in height_array.iter() { @@ -1639,8 +1577,7 @@ mod tests { random_enc_ciphertext_with(height, ivk, &mut rng); assert_eq!( - try_sapling_output_recovery( - &consensus::MainNetwork, + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1676,8 +1613,7 @@ mod tests { }; } - let height = consensus::MainNetwork - .activation_height(NetworkUpgrade::Sapling) + let height = Network::activation_height(NetworkUpgrade::Sapling) .expect("Should have Sapling activation height"); for tv in test_vectors { @@ -1726,14 +1662,7 @@ mod tests { // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption( - &consensus::MainNetwork, - height, - &ivk, - &epk, - &cmu, - &tv.c_enc, - ) { + match try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &tv.c_enc) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1742,8 +1671,7 @@ mod tests { None => panic!("Note decryption failed"), } - match try_sapling_compact_note_decryption( - &consensus::MainNetwork, + match try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1757,15 +1685,8 @@ mod tests { None => panic!("Compact note decryption failed"), } - match try_sapling_output_recovery( - &consensus::MainNetwork, - height, - &ovk, - &cv, - &cmu, - &epk, - &tv.c_enc, - &tv.c_out, + match try_sapling_output_recovery::( + height, &ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out, ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 89bf449005..68448f4f1a 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -25,7 +25,7 @@ use crate::{ components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut}, signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, - JUBJUB, + Network, JUBJUB, }; #[cfg(feature = "transparent-inputs")] @@ -88,7 +88,6 @@ pub struct SaplingOutput { impl SaplingOutput { pub fn new( - parameters: P, height: u32, rng: &mut R, ovk: OutgoingViewingKey, @@ -104,7 +103,7 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rseed = if parameters.is_nu_active(NetworkUpgrade::Canopy, height) { + let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) @@ -403,22 +402,14 @@ impl Builder { } /// Adds a Sapling address to send funds to. - pub fn add_sapling_output( + pub fn add_sapling_output( &mut self, ovk: OutgoingViewingKey, to: PaymentAddress, value: Amount, memo: Option, ) -> Result<(), Error> { - let output = SaplingOutput::new( - consensus::MainNetwork, - self.height, - &mut self.rng, - ovk, - to, - value, - memo, - )?; + let output = SaplingOutput::new::(self.height, &mut self.rng, ovk, to, value, memo)?; self.mtx.value_balance -= value; @@ -518,7 +509,7 @@ impl Builder { return Err(Error::NoChangeAddress); }; - self.add_sapling_output(change_address.0, change_address.1, change, None)?; + self.add_sapling_output::(change_address.0, change_address.1, change, None)?; } // @@ -733,7 +724,7 @@ mod tests { sapling::Node, transaction::components::Amount, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, + Network, JUBJUB, }; #[test] @@ -745,7 +736,7 @@ mod tests { let mut builder = Builder::new(0); assert_eq!( - builder.add_sapling_output(ovk, to, Amount::from_i64(-1).unwrap(), None), + builder.add_sapling_output::(ovk, to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) ); } @@ -758,9 +749,8 @@ mod tests { TransactionData, }; - let sapling_activation_height = consensus::MainNetwork - .activation_height(NetworkUpgrade::Sapling) - .unwrap(); + let sapling_activation_height = + Network::activation_height(NetworkUpgrade::Sapling).unwrap(); // Create a builder with 0 fee, so we can construct t outputs let mut builder = builder::Builder { @@ -865,7 +855,7 @@ mod tests { { let mut builder = Builder::new(0); builder - .add_sapling_output( + .add_sapling_output::( ovk.clone(), to.clone(), Amount::from_u64(50000).unwrap(), @@ -915,7 +905,7 @@ mod tests { ) .unwrap(); builder - .add_sapling_output( + .add_sapling_output::( ovk.clone(), to.clone(), Amount::from_u64(30000).unwrap(), @@ -961,7 +951,7 @@ mod tests { .add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap()) .unwrap(); builder - .add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None) + .add_sapling_output::(ovk, to, Amount::from_u64(30000).unwrap(), None) .unwrap(); builder .add_transparent_output( From d5f80618efa09d1399026078690c10f85a05730e Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 14:26:57 +0800 Subject: [PATCH 114/210] Switch plaintext version on height in commit_to_address() in zcash_client_sqlite --- zcash_client_sqlite/src/transact.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index e8c6700cf6..0ad359701f 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -9,6 +9,7 @@ use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ consensus, + consensus::Parameters, jubjub::fs::{Fs, FsRepr}, keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, @@ -234,14 +235,20 @@ pub fn create_to_address>( let note_value: i64 = row.get(1)?; - let rcm = { - let d: Vec<_> = row.get(2)?; + let d: Vec<_> = row.get(2)?; + + let rseed = if height >= Network::CANOPY_ACTIVATION_HEIGHT { + let mut r = [0u8; 32]; + r.copy_from_slice(&d[..]); + Rseed::AfterZip212(r) + } else { let tmp = FsRepr( d[..] .try_into() .map_err(|_| Error(ErrorKind::InvalidNote))?, ); - Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))? + let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?; + Rseed::BeforeZip212(r) }; let from = extfvk @@ -249,9 +256,7 @@ pub fn create_to_address>( .vk .to_payment_address(diversifier, &JUBJUB) .unwrap(); - let note = from - .create_note(note_value as u64, Rseed::BeforeZip212(rcm), &JUBJUB) - .unwrap(); + let note = from.create_note(note_value as u64, rseed, &JUBJUB).unwrap(); let merkle_path = { let d: Vec<_> = row.get(3)?; From a3ae1b273d2480ebd1ce23b65704afcf29e4bef2 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 14:27:36 +0800 Subject: [PATCH 115/210] Switch plaintext version on height for dummy outputs in Builder --- zcash_primitives/src/transaction/builder.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 68448f4f1a..280880b3ed 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -13,7 +13,7 @@ use std::fmt; use crate::{ consensus, - consensus::NetworkUpgrade, + consensus::{NetworkUpgrade, Parameters}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -620,12 +620,20 @@ impl Builder { } }; + let rseed = if self.height >= Network::CANOPY_ACTIVATION_HEIGHT { + let mut buffer = [0u8; 32]; + &mut self.rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(&mut self.rng)) + }; + ( payment_address, Note { g_d, pk_d, - rseed: Rseed::BeforeZip212(Fs::random(&mut self.rng)), + rseed, value: 0, }, ) From 2ed9b6f881f71388127826b5b5ad44be17e937af Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 15:21:42 +0800 Subject: [PATCH 116/210] Refactor contextual random rseed generation into util method in zcash_primitives --- zcash_client_backend/src/welding_rig.rs | 17 ++++---- zcash_client_sqlite/src/lib.rs | 44 +++++++++------------ zcash_primitives/src/lib.rs | 2 +- zcash_primitives/src/note_encryption.rs | 9 +---- zcash_primitives/src/transaction/builder.rs | 9 +---- zcash_primitives/src/util.rs | 23 ++++++++++- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index f51ef3eac4..e3b258f894 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -191,12 +191,13 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::{NetworkUpgrade, Parameters}, + consensus::NetworkUpgrade, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, - primitives::{Note, Rseed}, + primitives::Note, transaction::components::Amount, + util::generate_random_rseed, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, }; @@ -257,13 +258,11 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut rng)) - }; + let rseed = generate_random_rseed::( + NetworkUpgrade::Canopy, + height as u32, + &mut rng, + ); let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index df41cf4dd3..533a258094 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -95,7 +95,7 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error: #[cfg(test)] mod tests { use crate::Network; - use ff::{Field, PrimeField}; + use ff::PrimeField; use pairing::bls12_381::Bls12; use protobuf::Message; use rand_core::{OsRng, RngCore}; @@ -106,11 +106,11 @@ mod tests { }; use zcash_primitives::{ block::BlockHash, - consensus::{NetworkUpgrade, Parameters}, - jubjub::fs::Fs, + consensus::NetworkUpgrade, note_encryption::{Memo, SaplingNoteEncryption}, - primitives::{Note, PaymentAddress, Rseed}, + primitives::{Note, PaymentAddress}, transaction::components::Amount, + util::generate_random_rseed, zip32::ExtendedFullViewingKey, JUBJUB, }; @@ -127,13 +127,11 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut rng)) - }; + let rseed = generate_random_rseed::( + NetworkUpgrade::Canopy, + height as u32, + &mut rng, + ); let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), @@ -183,13 +181,11 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; - let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut rng)) - }; + let rseed = generate_random_rseed::( + NetworkUpgrade::Canopy, + height as u32, + &mut rng, + ); // Create a fake CompactBlock containing the note let mut cspend = CompactSpend::new(); @@ -226,13 +222,11 @@ mod tests { // Create a fake Note for the change ctx.outputs.push({ let change_addr = extfvk.default_address().unwrap().1; - let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height as u32) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut rng)) - }; + let rseed = generate_random_rseed::( + NetworkUpgrade::Canopy, + height as u32, + &mut rng, + ); let note = Note { g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: change_addr.pk_d().clone(), diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 2bbc801c29..2f23a6903c 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -24,7 +24,7 @@ pub mod redjubjub; pub mod sapling; pub mod serialize; pub mod transaction; -mod util; +pub mod util; pub mod zip32; #[cfg(test)] diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index cc52356c60..6ac0a50227 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -593,6 +593,7 @@ mod tests { PrimeOrder, Unknown, }, primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, + util::generate_random_rseed, Network, }; use crypto_api_chachapoly::ChachaPolyIetf; @@ -795,13 +796,7 @@ mod tests { }; let cv = value_commitment.cm(&JUBJUB).into(); - let rseed = if Network::is_nu_active(NetworkUpgrade::Canopy, height) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(rng)) - }; + let rseed = generate_random_rseed::(NetworkUpgrade::Canopy, height, &mut rng); let note = pa.create_note(value, rseed, &JUBJUB).unwrap(); let cmu = note.cm(&JUBJUB); diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 280880b3ed..ba5a9e4d94 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -25,6 +25,7 @@ use crate::{ components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut}, signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, + util::generate_random_rseed, Network, JUBJUB, }; @@ -103,13 +104,7 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { - let mut buffer = [0u8; 32]; - &rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(rng)) - }; + let rseed = generate_random_rseed::(NetworkUpgrade::Canopy, height, rng); let note = Note { g_d, diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index 1fdcde750e..65d3418a43 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -1,6 +1,13 @@ use blake2b_simd::Params; -use crate::jubjub::{JubjubEngine, ToUniform}; +use crate::{ + consensus, + consensus::NetworkUpgrade, + jubjub::{fs::Fs, JubjubEngine, ToUniform}, + primitives::Rseed, +}; +use ff::Field; +use rand_core::{CryptoRng, RngCore}; pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { let mut hasher = Params::new().hash_length(64).personal(persona).to_state(); @@ -9,3 +16,17 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E: let ret = hasher.finalize(); E::Fs::to_uniform(ret.as_ref()) } + +pub fn generate_random_rseed( + nu: NetworkUpgrade, + height: u32, + rng: &mut R, +) -> Rseed { + if P::is_nu_active(nu, height) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(Fs::random(rng)) + } +} From c8fcdeb50b3effe200d9b14f705fb5a35aac8abb Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 15:45:09 +0800 Subject: [PATCH 117/210] Minor changes in note_encryption.rs --- zcash_primitives/src/note_encryption.rs | 138 ++++++++++-------------- 1 file changed, 57 insertions(+), 81 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 6ac0a50227..1a88b367e7 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -198,6 +198,7 @@ fn prf_ock( /// are consistent with each other. /// /// Implements section 4.17.1 of the Zcash Protocol Specification. +/// NB: the example code is only covering the pre-Canopy case. /// /// # Examples /// @@ -226,11 +227,12 @@ fn prf_ock( /// let ovk = OutgoingViewingKey([0; 32]); /// /// let value = 1000; -/// let rcm = Fs::random(&mut rng); +/// let rcv = Fs::random(&mut rng); /// let cv = ValueCommitment:: { /// value, -/// randomness: rcm.clone(), +/// randomness: rcv.clone(), /// }; +/// let rcm = Fs::random(&mut rng); /// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap(); /// let cmu = note.cm(&JUBJUB); /// @@ -347,9 +349,8 @@ fn parse_note_plaintext_without_memo( plaintext: &[u8], ) -> Option<(Note, PaymentAddress)> { // Check note plaintext version - match plaintext_version_is_valid::

(height, plaintext[0]) { - true => (), - false => return None, + if !plaintext_version_is_valid::

(height, plaintext[0]) { + return None; } let mut d = [0u8; 11]; @@ -539,9 +540,8 @@ pub fn try_sapling_output_recovery( ); // Check note plaintext version - match plaintext_version_is_valid::

(height, plaintext[0]) { - true => (), - false => return None, + if !plaintext_version_is_valid::

(height, plaintext[0]) { + return None; } let mut d = [0u8; 11]; @@ -908,13 +908,12 @@ mod tests { #[test] fn decryption_with_invalid_ivk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -933,13 +932,12 @@ mod tests { #[test] fn decryption_with_invalid_epk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -958,13 +956,12 @@ mod tests { #[test] fn decryption_with_invalid_cmu() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -983,13 +980,12 @@ mod tests { #[test] fn decryption_with_invalid_tag() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, ivk, _, cmu, epk, mut enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); @@ -1004,15 +1000,14 @@ mod tests { #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::CANOPY_ACTIVATION_HEIGHT - 1, Network::CANOPY_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; - for (i, height_ref) in height_array.iter().enumerate() { - let height = *height_ref; + for (i, &height) in heights.iter().enumerate() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1035,13 +1030,12 @@ mod tests { #[test] fn decryption_with_invalid_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1064,13 +1058,12 @@ mod tests { #[test] fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1093,13 +1086,12 @@ mod tests { #[test] fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1118,13 +1110,12 @@ mod tests { #[test] fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1143,13 +1134,12 @@ mod tests { #[test] fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1168,15 +1158,14 @@ mod tests { #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::CANOPY_ACTIVATION_HEIGHT - 1, Network::CANOPY_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; - for (i, height_ref) in height_array.iter().enumerate() { - let height = *height_ref; + for (i, &height) in heights.iter().enumerate() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1205,13 +1194,12 @@ mod tests { #[test] fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1240,13 +1228,12 @@ mod tests { #[test] fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1275,13 +1262,12 @@ mod tests { #[test] fn recovery_with_invalid_ovk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (mut ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1304,13 +1290,12 @@ mod tests { #[test] fn recovery_with_invalid_cv() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1332,14 +1317,13 @@ mod tests { #[test] fn recovery_with_invalid_cmu() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; - let (ovk, _, cv, _, epk, enc_ciphertext, out_ciphertext) = + for &height in heights.iter() { + let (ovk, _, cv, _, epk, enc_ctext, out_ctext) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1349,8 +1333,8 @@ mod tests { &cv, &Fr::random(&mut rng), &epk, - &enc_ciphertext, - &out_ciphertext + &enc_ctext, + &out_ctext ), None ); @@ -1360,13 +1344,12 @@ mod tests { #[test] fn recovery_with_invalid_epk() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1388,13 +1371,12 @@ mod tests { #[test] fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1417,13 +1399,12 @@ mod tests { #[test] fn recovery_with_invalid_out_tag() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1446,15 +1427,14 @@ mod tests { #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::CANOPY_ACTIVATION_HEIGHT - 1, Network::CANOPY_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; - for (i, height_ref) in height_array.iter().enumerate() { - let height = *height_ref; + for (i, &height) in heights.iter().enumerate() { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1485,13 +1465,12 @@ mod tests { #[test] fn recovery_with_invalid_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1522,14 +1501,12 @@ mod tests { #[test] fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; - + for &height in heights.iter() { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1560,13 +1537,12 @@ mod tests { #[test] fn recovery_with_invalid_pk_d() { let mut rng = OsRng; - let height_array = [ + let heights = [ Network::SAPLING_ACTIVATION_HEIGHT, Network::CANOPY_ACTIVATION_HEIGHT, ]; - for height_ref in height_array.iter() { - let height = *height_ref; + for &height in heights.iter() { let ivk = Fs::zero(); let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, &mut rng); From d6deaddc0a32e5c7dedf2832b8dca22bb0a922a5 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 5 Aug 2020 16:09:41 +0800 Subject: [PATCH 118/210] Only query last_height when needed in decrypt_and_store_transaction() --- zcash_client_sqlite/src/scan.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 417d63bef4..d22a486620 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -373,12 +373,15 @@ pub fn decrypt_and_store_transaction>( .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; // Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions. - let last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) - })?; let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?; let height = stmt_select_block.query_row(&[tx.txid().0.to_vec()], |row| { - row.get(0).or(Ok(last_height + 1)) + row.get(0).or({ + let last_height = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + Ok(last_height + 1) + }) })?; let outputs = decrypt_transaction::(height as u32, tx, &extfvks); From 139fc09f1028f0f2d073748dfca8529dbe98807b Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 5 Aug 2020 03:26:31 -0700 Subject: [PATCH 119/210] bellman: add VerificationError (#254) * bellman: add VerificationError This adds a distinct VerificationError type to the crate and changes `verify_proof` to return `Result<(), VerificationError>` rather than `Result`. This is significantly safer, because it avoids the need to mix pattern-matching logic with boolean logic (the cause of RUSTSEC-2019-0004). * Rename VerificationError variants per review comments. * Add missing Clone impl to VerificationError. --- bellman/src/groth16/mod.rs | 4 ++-- bellman/src/groth16/tests/mod.rs | 2 +- bellman/src/groth16/verifier.rs | 25 +++++++++++++--------- bellman/src/lib.rs | 31 +++++++++++++++++++++++----- bellman/tests/mimc.rs | 2 +- zcash_proofs/src/sapling/prover.rs | 10 +-------- zcash_proofs/src/sapling/verifier.rs | 16 ++------------ zcash_proofs/src/sprout.rs | 8 +------ 8 files changed, 49 insertions(+), 49 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 7c97197aaa..624324f2af 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -560,8 +560,8 @@ mod test_with_bls12_381 { let de_proof = Proof::read(&v[..]).unwrap(); assert!(proof == de_proof); - assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); - assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[c]).is_ok()); + assert!(verify_proof(&pvk, &proof, &[a]).is_err()); } } } diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index 8038e1721f..371f7c16fa 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -377,5 +377,5 @@ fn test_xordemo() { assert_eq!(expected_c, proof.c); } - assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[Fr::one()]).is_ok()); } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 0fe8c94609..18a376db6f 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -4,7 +4,7 @@ use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; -use crate::SynthesisError; +use crate::VerificationError; pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { let gamma = vk.gamma_g2.neg(); @@ -22,9 +22,9 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( pvk: &'a PreparedVerifyingKey, proof: &Proof, public_inputs: &[E::Fr], -) -> Result { +) -> Result<(), VerificationError> { if (public_inputs.len() + 1) != pvk.ic.len() { - return Err(SynthesisError::MalformedVerifyingKey); + return Err(VerificationError::InvalidVerifyingKey); } let mut acc = pvk.ic[0].to_curve(); @@ -41,11 +41,16 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // which allows us to do a single final exponentiation. - Ok(E::multi_miller_loop(&[ - (&proof.a, &proof.b.into()), - (&acc.to_affine(), &pvk.neg_gamma_g2), - (&proof.c, &pvk.neg_delta_g2), - ]) - .final_exponentiation() - == pvk.alpha_g1_beta_g2) + if pvk.alpha_g1_beta_g2 + == E::multi_miller_loop(&[ + (&proof.a, &proof.b.into()), + (&acc.to_affine(), &pvk.neg_gamma_g2), + (&proof.c, &pvk.neg_delta_g2), + ]) + .final_exponentiation() + { + Ok(()) + } else { + Err(VerificationError::InvalidProof) + } } diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index 6412a928bf..89e7f8bd94 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -122,7 +122,7 @@ //! let inputs = multipack::compute_multipacking(&hash_bits); //! //! // Check the proof! -//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap()); +//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok()); //! ``` //! //! # Roadmap @@ -301,7 +301,7 @@ impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination)> } /// This is an error that could occur during circuit synthesis contexts, -/// such as CRS generation, proving or verification. +/// such as CRS generation or proving. #[derive(Debug)] pub enum SynthesisError { /// During synthesis, we lacked knowledge of a variable assignment. @@ -316,8 +316,6 @@ pub enum SynthesisError { UnexpectedIdentity, /// During proof generation, we encountered an I/O error with the CRS IoError(io::Error), - /// During verification, our verifying key was malformed. - MalformedVerifyingKey, /// During CRS generation, we observed an unconstrained auxiliary variable UnconstrainedVariable, } @@ -339,7 +337,6 @@ impl Error for SynthesisError { SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", SynthesisError::IoError(_) => "encountered an I/O error", - SynthesisError::MalformedVerifyingKey => "malformed verifying key", SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained", } } @@ -356,6 +353,30 @@ impl fmt::Display for SynthesisError { } } +/// An error during verification. +#[derive(Debug, Clone)] +pub enum VerificationError { + /// Verification was attempted with a malformed verifying key. + InvalidVerifyingKey, + /// Proof verification failed. + InvalidProof, +} + +impl Error for VerificationError { + fn description(&self) -> &str { + match *self { + VerificationError::InvalidVerifyingKey => "malformed verifying key", + VerificationError::InvalidProof => "proof verification failed", + } + } +} + +impl fmt::Display for VerificationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + /// Represents a constraint system which can have new variables /// allocated and constrains between them formed. pub trait ConstraintSystem: Sized { diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs index b708a18a22..269690840c 100644 --- a/bellman/tests/mimc.rs +++ b/bellman/tests/mimc.rs @@ -210,7 +210,7 @@ fn test_mimc() { let start = Instant::now(); let proof = Proof::read(&proof_vec[..]).unwrap(); // Check the proof - assert!(verify_proof(&pvk, &proof, &[image]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[image]).is_ok()); total_verifying += start.elapsed(); } let proving_avg = total_proving / SAMPLES; diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index cc3898e064..06f9d3ac87 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -154,15 +154,7 @@ impl SaplingProvingContext { } // Verify the proof - match verify_proof(verifying_key, &proof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => {} - - // Any other case - _ => { - return Err(()); - } - } + verify_proof(verifying_key, &proof, &public_input[..]).map_err(|_| ())?; // Compute value commitment let value_commitment: edwards::Point = value_commitment.cm(params).into(); diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index 32e4d8ca86..2a1fffaa1a 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -106,13 +106,7 @@ impl SaplingVerificationContext { } // Verify the proof - match verify_proof(verifying_key, &zkproof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() } /// Perform consensus checks on a Sapling OutputDescription, while @@ -159,13 +153,7 @@ impl SaplingVerificationContext { public_input[4] = cm; // Verify the proof - match verify_proof(verifying_key, &zkproof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() } /// Perform consensus checks on the valueBalance and bindingSig parts of a diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs index efd0508eca..d6646b5ce1 100644 --- a/zcash_proofs/src/sprout.rs +++ b/zcash_proofs/src/sprout.rs @@ -169,11 +169,5 @@ pub fn verify_proof( }; // Verify the proof - match groth16::verify_proof(verifying_key, &proof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + groth16::verify_proof(verifying_key, &proof, &public_input[..]).is_ok() } From 88072d64ecfb3250ce85511996e8d317349fa9af Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 11:35:05 +0800 Subject: [PATCH 120/210] Remove network cfg from zcash_client_backend and zcash_primitives --- zcash_client_backend/src/lib.rs | 6 - zcash_client_backend/src/welding_rig.rs | 15 +- zcash_primitives/src/lib.rs | 6 - zcash_primitives/src/note_encryption.rs | 198 ++++++++++++++---------- 4 files changed, 119 insertions(+), 106 deletions(-) diff --git a/zcash_client_backend/src/lib.rs b/zcash_client_backend/src/lib.rs index a4e5fb4229..e3852e6acc 100644 --- a/zcash_client_backend/src/lib.rs +++ b/zcash_client_backend/src/lib.rs @@ -15,9 +15,3 @@ pub mod wallet; pub mod welding_rig; pub use decrypt::{decrypt_transaction, DecryptedOutput}; - -#[cfg(feature = "mainnet")] -pub use zcash_primitives::consensus::MainNetwork as Network; - -#[cfg(not(feature = "mainnet"))] -pub use zcash_primitives::consensus::TestNetwork as Network; diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index e3b258f894..c07001cdc8 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -191,7 +191,7 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::NetworkUpgrade, + consensus::{NetworkUpgrade, TestNetwork}, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, @@ -203,10 +203,7 @@ mod tests { }; use super::scan_block; - use crate::{ - proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx}, - Network, - }; + use crate::proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx}; fn random_compact_tx(rng: &mut R) -> CompactTx { let fake_nf = { @@ -258,7 +255,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::( + let rseed = generate_random_rseed::( NetworkUpgrade::Canopy, height as u32, &mut rng, @@ -333,7 +330,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -365,7 +362,7 @@ mod tests { assert_eq!(cb.vtx.len(), 3); let mut tree = CommitmentTree::new(); - let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -393,7 +390,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block::(cb, &[], &[(&nf, account)], &mut tree, &mut []); + let txs = scan_block::(cb, &[], &[(&nf, account)], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 2f23a6903c..fefbad07e2 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -35,9 +35,3 @@ use crate::jubjub::JubjubBls12; lazy_static! { pub static ref JUBJUB: JubjubBls12 = JubjubBls12::new(); } - -#[cfg(feature = "mainnet")] -use crate::consensus::MainNetwork as Network; - -#[cfg(not(feature = "mainnet"))] -use crate::consensus::TestNetwork as Network; diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 1a88b367e7..e85daaede0 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -586,7 +586,7 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { use crate::{ - consensus::{NetworkUpgrade, Parameters, ZIP212_GRACE_PERIOD}, + consensus::{NetworkUpgrade, Parameters, TestNetwork, ZIP212_GRACE_PERIOD}, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -594,7 +594,6 @@ mod tests { }, primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, util::generate_random_rseed, - Network, }; use crypto_api_chachapoly::ChachaPolyIetf; use ff::{Field, PrimeField}; @@ -745,11 +744,15 @@ mod tests { let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, rng); - assert!( - try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext) - .is_some() - ); - assert!(try_sapling_compact_note_decryption::( + assert!(try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ) + .is_some()); + assert!(try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -757,7 +760,7 @@ mod tests { &enc_ciphertext[..COMPACT_NOTE_SIZE] ) .is_some()); - assert!(try_sapling_output_recovery::( + assert!(try_sapling_output_recovery::( height, &ovk, &cv, @@ -796,7 +799,8 @@ mod tests { }; let cv = value_commitment.cm(&JUBJUB).into(); - let rseed = generate_random_rseed::(NetworkUpgrade::Canopy, height, &mut rng); + let rseed = + generate_random_rseed::(NetworkUpgrade::Canopy, height, &mut rng); let note = pa.create_note(value, rseed, &JUBJUB).unwrap(); let cmu = note.cm(&JUBJUB); @@ -909,15 +913,15 @@ mod tests { fn decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption::( height, &Fs::random(&mut rng), &epk, @@ -933,15 +937,15 @@ mod tests { fn decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption::( height, &ivk, &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), @@ -957,15 +961,15 @@ mod tests { fn decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption::( height, &ivk, &epk, @@ -981,8 +985,8 @@ mod tests { fn decryption_with_invalid_tag() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -991,7 +995,13 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), None ); } @@ -1001,9 +1011,9 @@ mod tests { fn decryption_with_invalid_version_byte() { let mut rng = OsRng; let heights = [ - Network::CANOPY_ACTIVATION_HEIGHT - 1, - Network::CANOPY_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1021,7 +1031,13 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), None ); } @@ -1031,8 +1047,8 @@ mod tests { fn decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1049,7 +1065,13 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), None ); } @@ -1059,8 +1081,8 @@ mod tests { fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1077,7 +1099,13 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &enc_ciphertext), + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), None ); } @@ -1087,15 +1115,15 @@ mod tests { fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &Fs::random(&mut rng), &epk, @@ -1111,15 +1139,15 @@ mod tests { fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &ivk, &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), @@ -1135,15 +1163,15 @@ mod tests { fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1159,9 +1187,9 @@ mod tests { fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; let heights = [ - Network::CANOPY_ACTIVATION_HEIGHT - 1, - Network::CANOPY_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1179,7 +1207,7 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1195,8 +1223,8 @@ mod tests { fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1213,7 +1241,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1229,8 +1257,8 @@ mod tests { fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1247,7 +1275,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1263,8 +1291,8 @@ mod tests { fn recovery_with_invalid_ovk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1273,7 +1301,7 @@ mod tests { ovk.0[0] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1291,8 +1319,8 @@ mod tests { fn recovery_with_invalid_cv() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1300,7 +1328,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &edwards::Point::::rand(&mut rng, &JUBJUB), @@ -1318,8 +1346,8 @@ mod tests { fn recovery_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1327,7 +1355,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1345,8 +1373,8 @@ mod tests { fn recovery_with_invalid_epk() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1354,7 +1382,7 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1372,8 +1400,8 @@ mod tests { fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1382,7 +1410,7 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1400,8 +1428,8 @@ mod tests { fn recovery_with_invalid_out_tag() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1410,7 +1438,7 @@ mod tests { out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1428,9 +1456,9 @@ mod tests { fn recovery_with_invalid_version_byte() { let mut rng = OsRng; let heights = [ - Network::CANOPY_ACTIVATION_HEIGHT - 1, - Network::CANOPY_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1448,7 +1476,7 @@ mod tests { |pt| pt[0] = leadbyte_array[i], ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1466,8 +1494,8 @@ mod tests { fn recovery_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1484,7 +1512,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1502,8 +1530,8 @@ mod tests { fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1520,7 +1548,7 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1538,8 +1566,8 @@ mod tests { fn recovery_with_invalid_pk_d() { let mut rng = OsRng; let heights = [ - Network::SAPLING_ACTIVATION_HEIGHT, - Network::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::SAPLING_ACTIVATION_HEIGHT, + TestNetwork::CANOPY_ACTIVATION_HEIGHT, ]; for &height in heights.iter() { @@ -1548,7 +1576,7 @@ mod tests { random_enc_ciphertext_with(height, ivk, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery::( height, &ovk, &cv, @@ -1584,7 +1612,7 @@ mod tests { }; } - let height = Network::activation_height(NetworkUpgrade::Sapling) + let height = TestNetwork::activation_height(NetworkUpgrade::Sapling) .expect("Should have Sapling activation height"); for tv in test_vectors { @@ -1633,7 +1661,7 @@ mod tests { // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &tv.c_enc) { + match try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &tv.c_enc) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1642,7 +1670,7 @@ mod tests { None => panic!("Note decryption failed"), } - match try_sapling_compact_note_decryption::( + match try_sapling_compact_note_decryption::( height, &ivk, &epk, @@ -1656,7 +1684,7 @@ mod tests { None => panic!("Compact note decryption failed"), } - match try_sapling_output_recovery::( + match try_sapling_output_recovery::( height, &ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out, ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { From 44f46e50ce05a5c9fcc78a4f2129fccad13f4247 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 11:36:02 +0800 Subject: [PATCH 121/210] Add type parameter to Builder struct --- zcash_client_sqlite/src/transact.rs | 4 +- zcash_primitives/src/transaction/builder.rs | 81 +++++++++++++-------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 0ad359701f..18a8500738 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -286,7 +286,7 @@ pub fn create_to_address>( } // Create the transaction - let mut builder = Builder::new(height); + let mut builder = Builder::::new(height); for selected in notes { builder.add_sapling_spend( extsk.clone(), @@ -297,7 +297,7 @@ pub fn create_to_address>( } match to { RecipientAddress::Shielded(to) => { - builder.add_sapling_output::(ovk, to.clone(), value, memo.clone()) + builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) } RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), }?; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index ba5a9e4d94..8497fea811 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -3,17 +3,18 @@ use crate::zip32::ExtendedSpendingKey; use crate::{ jubjub::fs::Fs, - primitives::{Diversifier, Note, PaymentAddress, Rseed}, + primitives::{Diversifier, Note, PaymentAddress}, }; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; use std::error; use std::fmt; +use std::marker::PhantomData; use crate::{ consensus, - consensus::{NetworkUpgrade, Parameters}, + consensus::{MainNetwork, NetworkUpgrade, TestNetwork}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -26,7 +27,7 @@ use crate::{ signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, util::generate_random_rseed, - Network, JUBJUB, + JUBJUB, }; #[cfg(feature = "transparent-inputs")] @@ -305,7 +306,7 @@ impl TransactionMetadata { } /// Generates a [`Transaction`] from its inputs and outputs. -pub struct Builder { +pub struct Builder { rng: R, height: u32, mtx: TransactionData, @@ -315,9 +316,10 @@ pub struct Builder { outputs: Vec, transparent_inputs: TransparentInputs, change_address: Option<(OutgoingViewingKey, PaymentAddress)>, + phantom: PhantomData

, } -impl Builder { +impl Builder { /// Creates a new `Builder` targeted for inclusion in the block with the given height, /// using default values for general transaction fields and the default OS random. /// @@ -332,7 +334,22 @@ impl Builder { } } -impl Builder { +impl Builder { + /// Creates a new `Builder` targeted for inclusion in the block with the given height, + /// using default values for general transaction fields and the default OS random. + /// + /// # Default values + /// + /// The expiry height will be set to the given height plus the default transaction + /// expiry delta (20 blocks). + /// + /// The fee will be set to the default fee (0.0001 ZEC). + pub fn new(height: u32) -> Self { + Builder::new_with_rng(height, OsRng) + } +} + +impl Builder { /// Creates a new `Builder` targeted for inclusion in the block with the given height /// and randomness source, using default values for general transaction fields. /// @@ -342,7 +359,7 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: u32, rng: R) -> Builder { + pub fn new_with_rng(height: u32, rng: R) -> Builder { let mut mtx = TransactionData::new(); mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; @@ -356,6 +373,7 @@ impl Builder { outputs: vec![], transparent_inputs: TransparentInputs::default(), change_address: None, + phantom: PhantomData, } } @@ -397,7 +415,7 @@ impl Builder { } /// Adds a Sapling address to send funds to. - pub fn add_sapling_output( + pub fn add_sapling_output( &mut self, ovk: OutgoingViewingKey, to: PaymentAddress, @@ -504,7 +522,7 @@ impl Builder { return Err(Error::NoChangeAddress); }; - self.add_sapling_output::(change_address.0, change_address.1, change, None)?; + self.add_sapling_output(change_address.0, change_address.1, change, None)?; } // @@ -615,13 +633,11 @@ impl Builder { } }; - let rseed = if self.height >= Network::CANOPY_ACTIVATION_HEIGHT { - let mut buffer = [0u8; 32]; - &mut self.rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) - } else { - Rseed::BeforeZip212(Fs::random(&mut self.rng)) - }; + let rseed = generate_random_rseed::( + NetworkUpgrade::Canopy, + self.height, + &mut self.rng, + ); ( payment_address, @@ -714,12 +730,14 @@ impl Builder { mod tests { use ff::{Field, PrimeField}; use rand_core::OsRng; + use std::marker::PhantomData; use crate::jubjub::fs::Fs; use super::{Builder, Error}; use crate::{ consensus, + consensus::TestNetwork, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -727,7 +745,7 @@ mod tests { sapling::Node, transaction::components::Amount, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - Network, JUBJUB, + JUBJUB, }; #[test] @@ -737,9 +755,9 @@ mod tests { let ovk = extfvk.fvk.ovk; let to = extfvk.default_address().unwrap().1; - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); assert_eq!( - builder.add_sapling_output::(ovk, to, Amount::from_i64(-1).unwrap(), None), + builder.add_sapling_output(ovk, to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) ); } @@ -753,10 +771,10 @@ mod tests { }; let sapling_activation_height = - Network::activation_height(NetworkUpgrade::Sapling).unwrap(); + TestNetwork::activation_height(NetworkUpgrade::Sapling).unwrap(); // Create a builder with 0 fee, so we can construct t outputs - let mut builder = builder::Builder { + let mut builder = builder::Builder:: { rng: OsRng, height: sapling_activation_height, mtx: TransactionData::new(), @@ -766,6 +784,7 @@ mod tests { outputs: vec![], transparent_inputs: TransparentInputs::default(), change_address: None, + phantom: PhantomData, }; // Create a tx with only t output. No binding_sig should be present @@ -796,7 +815,7 @@ mod tests { tree.append(cm1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); // Create a tx with a sapling spend. binding_sig should be present builder @@ -822,7 +841,7 @@ mod tests { #[test] fn fails_on_negative_transparent_output() { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -842,7 +861,7 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::new(0); + let builder = Builder::::new(0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) @@ -856,9 +875,9 @@ mod tests { // Fail if there is only a Sapling output // 0.0005 z-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder - .add_sapling_output::( + .add_sapling_output( ovk.clone(), to.clone(), Amount::from_u64(50000).unwrap(), @@ -874,7 +893,7 @@ mod tests { // Fail if there is only a transparent output // 0.0005 t-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -898,7 +917,7 @@ mod tests { // Fail if there is insufficient input // 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_sapling_spend( extsk.clone(), @@ -908,7 +927,7 @@ mod tests { ) .unwrap(); builder - .add_sapling_output::( + .add_sapling_output( ovk.clone(), to.clone(), Amount::from_u64(30000).unwrap(), @@ -941,7 +960,7 @@ mod tests { // (Still fails because we are using a MockTxProver which doesn't correctly // compute bindingSig.) { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_sapling_spend( extsk.clone(), @@ -954,7 +973,7 @@ mod tests { .add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap()) .unwrap(); builder - .add_sapling_output::(ovk, to, Amount::from_u64(30000).unwrap(), None) + .add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None) .unwrap(); builder .add_transparent_output( From b05e257f79f878b0ea1dbd4c412c6d964f3d116a Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 12:00:49 +0800 Subject: [PATCH 122/210] Fix database queries in scan.rs and transact.rs --- zcash_client_sqlite/src/scan.rs | 24 +++++++++++++--------- zcash_client_sqlite/src/transact.rs | 32 +++++++++++++++-------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index d22a486620..a32df41e6b 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -2,7 +2,7 @@ use ff::PrimeField; use protobuf::parse_from_bytes; -use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use rusqlite::{types::ToSql, Connection, OptionalExtension, NO_PARAMS}; use std::path::Path; use zcash_client_backend::{ decrypt_transaction, encoding::decode_extended_full_viewing_key, @@ -374,15 +374,19 @@ pub fn decrypt_and_store_transaction>( // Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions. let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?; - let height = stmt_select_block.query_row(&[tx.txid().0.to_vec()], |row| { - row.get(0).or({ - let last_height = - data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) - })?; - Ok(last_height + 1) - }) - })?; + let height = match stmt_select_block + .query_row(&[tx.txid().0.to_vec()], |row| row.get(0)) + .optional()? + { + Some(height) => height, + None => data + .query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0) + }) + .optional()? + .map(|last_height: u32| last_height + 1) + .unwrap_or(SAPLING_ACTIVATION_HEIGHT as u32), + }; let outputs = decrypt_transaction::(height as u32, tx, &extfvks); diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 18a8500738..9df019bab5 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -9,7 +9,7 @@ use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ consensus, - consensus::Parameters, + consensus::{NetworkUpgrade, Parameters}, jubjub::fs::{Fs, FsRepr}, keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, @@ -235,20 +235,22 @@ pub fn create_to_address>( let note_value: i64 = row.get(1)?; - let d: Vec<_> = row.get(2)?; - - let rseed = if height >= Network::CANOPY_ACTIVATION_HEIGHT { - let mut r = [0u8; 32]; - r.copy_from_slice(&d[..]); - Rseed::AfterZip212(r) - } else { - let tmp = FsRepr( - d[..] - .try_into() - .map_err(|_| Error(ErrorKind::InvalidNote))?, - ); - let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?; - Rseed::BeforeZip212(r) + let rseed = { + let d: Vec<_> = row.get(2)?; + + if Network::is_nu_active(NetworkUpgrade::Canopy, height) { + let mut r = [0u8; 32]; + r.copy_from_slice(&d[..]); + Rseed::AfterZip212(r) + } else { + let tmp = FsRepr( + d[..] + .try_into() + .map_err(|_| Error(ErrorKind::InvalidNote))?, + ); + let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?; + Rseed::BeforeZip212(r) + } }; let from = extfvk From 9970a8aefde5eecc3ea3d858cf909d82fecb65d8 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 12:06:09 +0800 Subject: [PATCH 123/210] Hard-code NetworkUpgrade::Canopy in generate_random_rseed --- zcash_client_backend/src/welding_rig.rs | 8 ++------ zcash_client_sqlite/src/lib.rs | 19 +++---------------- zcash_primitives/src/note_encryption.rs | 3 +-- zcash_primitives/src/transaction/builder.rs | 10 +++------- zcash_primitives/src/util.rs | 3 +-- 5 files changed, 10 insertions(+), 33 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index c07001cdc8..fcb9c7a096 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -191,7 +191,7 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::{NetworkUpgrade, TestNetwork}, + consensus::{TestNetwork}, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, @@ -255,11 +255,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::( - NetworkUpgrade::Canopy, - height as u32, - &mut rng, - ); + let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 533a258094..655ae89e99 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -106,7 +106,6 @@ mod tests { }; use zcash_primitives::{ block::BlockHash, - consensus::NetworkUpgrade, note_encryption::{Memo, SaplingNoteEncryption}, primitives::{Note, PaymentAddress}, transaction::components::Amount, @@ -127,11 +126,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::( - NetworkUpgrade::Canopy, - height as u32, - &mut rng, - ); + let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: to.pk_d().clone(), @@ -181,11 +176,7 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; - let rseed = generate_random_rseed::( - NetworkUpgrade::Canopy, - height as u32, - &mut rng, - ); + let rseed = generate_random_rseed::(height as u32, &mut rng); // Create a fake CompactBlock containing the note let mut cspend = CompactSpend::new(); @@ -222,11 +213,7 @@ mod tests { // Create a fake Note for the change ctx.outputs.push({ let change_addr = extfvk.default_address().unwrap().1; - let rseed = generate_random_rseed::( - NetworkUpgrade::Canopy, - height as u32, - &mut rng, - ); + let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), pk_d: change_addr.pk_d().clone(), diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index e85daaede0..5d91321add 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -799,8 +799,7 @@ mod tests { }; let cv = value_commitment.cm(&JUBJUB).into(); - let rseed = - generate_random_rseed::(NetworkUpgrade::Canopy, height, &mut rng); + let rseed = generate_random_rseed::(height, &mut rng); let note = pa.create_note(value, rseed, &JUBJUB).unwrap(); let cmu = note.cm(&JUBJUB); diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 8497fea811..9ace406560 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -14,7 +14,7 @@ use std::marker::PhantomData; use crate::{ consensus, - consensus::{MainNetwork, NetworkUpgrade, TestNetwork}, + consensus::{MainNetwork, TestNetwork}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -105,7 +105,7 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rseed = generate_random_rseed::(NetworkUpgrade::Canopy, height, rng); + let rseed = generate_random_rseed::(height, rng); let note = Note { g_d, @@ -633,11 +633,7 @@ impl Builder { } }; - let rseed = generate_random_rseed::( - NetworkUpgrade::Canopy, - self.height, - &mut self.rng, - ); + let rseed = generate_random_rseed::(self.height, &mut self.rng); ( payment_address, diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index 65d3418a43..4edfbc7594 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -18,11 +18,10 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E: } pub fn generate_random_rseed( - nu: NetworkUpgrade, height: u32, rng: &mut R, ) -> Rseed { - if P::is_nu_active(nu, height) { + if P::is_nu_active(NetworkUpgrade::Canopy, height) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) From 4f22077cf6adf8df42ec9e78fbb6f1335c7cc3af Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 12:30:48 +0800 Subject: [PATCH 124/210] Remove const activation heights from consensus.rs --- zcash_client_backend/src/welding_rig.rs | 2 +- zcash_primitives/src/consensus.rs | 22 +---- zcash_primitives/src/note_encryption.rs | 107 +++++++++++++----------- 3 files changed, 60 insertions(+), 71 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index fcb9c7a096..b37ee13f0a 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -191,7 +191,7 @@ mod tests { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::{TestNetwork}, + consensus::TestNetwork, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 7020bae6e2..4cc67f49b5 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -13,12 +13,6 @@ pub trait Parameters { _ => false, } } - - const OVERWINTER_ACTIVATION_HEIGHT: u32; - const SAPLING_ACTIVATION_HEIGHT: u32; - const BLOSSOM_ACTIVATION_HEIGHT: u32; - const HEARTWOOD_ACTIVATION_HEIGHT: u32; - const CANOPY_ACTIVATION_HEIGHT: u32; } /// Marker struct for the production network. @@ -35,12 +29,6 @@ impl Parameters for MainNetwork { NetworkUpgrade::Canopy => Some(1_046_400), } } - - const OVERWINTER_ACTIVATION_HEIGHT: u32 = 347_500; - const SAPLING_ACTIVATION_HEIGHT: u32 = 419_200; - const BLOSSOM_ACTIVATION_HEIGHT: u32 = 653_600; - const HEARTWOOD_ACTIVATION_HEIGHT: u32 = 903_000; - const CANOPY_ACTIVATION_HEIGHT: u32 = 1_046_400; } /// Marker struct for the test network. @@ -57,12 +45,6 @@ impl Parameters for TestNetwork { NetworkUpgrade::Canopy => Some(1_028_500), } } - - const OVERWINTER_ACTIVATION_HEIGHT: u32 = 207_500; - const SAPLING_ACTIVATION_HEIGHT: u32 = 280_200; - const BLOSSOM_ACTIVATION_HEIGHT: u32 = 584_000; - const HEARTWOOD_ACTIVATION_HEIGHT: u32 = 903_800; - const CANOPY_ACTIVATION_HEIGHT: u32 = 1_028_500; } /// An event that occurs at a specified height on the Zcash chain, at which point the @@ -208,10 +190,10 @@ impl BranchId { #[cfg(test)] mod tests { - use super::{BranchId, MainNetwork, NetworkUpgrade, UPGRADES_IN_ORDER}; - use crate::consensus::Parameters; use std::convert::TryFrom; + use super::{BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; + #[test] fn nu_ordering() { for i in 1..UPGRADES_IN_ORDER.len() { diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 5d91321add..7a94fc66a5 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -586,7 +586,11 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { use crate::{ - consensus::{NetworkUpgrade, Parameters, TestNetwork, ZIP212_GRACE_PERIOD}, + consensus::{ + NetworkUpgrade, + NetworkUpgrade::{Canopy, Sapling}, + Parameters, TestNetwork, ZIP212_GRACE_PERIOD, + }, jubjub::{ edwards, fs::{Fs, FsRepr}, @@ -912,8 +916,8 @@ mod tests { fn decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -936,8 +940,8 @@ mod tests { fn decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -960,8 +964,8 @@ mod tests { fn decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -984,8 +988,8 @@ mod tests { fn decryption_with_invalid_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1009,10 +1013,11 @@ mod tests { #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); let heights = [ - TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1046,8 +1051,8 @@ mod tests { fn decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1080,8 +1085,8 @@ mod tests { fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1114,8 +1119,8 @@ mod tests { fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1138,8 +1143,8 @@ mod tests { fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1162,8 +1167,8 @@ mod tests { fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1185,10 +1190,11 @@ mod tests { #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); let heights = [ - TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1222,8 +1228,8 @@ mod tests { fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1256,8 +1262,8 @@ mod tests { fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1290,8 +1296,8 @@ mod tests { fn recovery_with_invalid_ovk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1318,8 +1324,8 @@ mod tests { fn recovery_with_invalid_cv() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1345,8 +1351,8 @@ mod tests { fn recovery_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1372,8 +1378,8 @@ mod tests { fn recovery_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1399,8 +1405,8 @@ mod tests { fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1427,8 +1433,8 @@ mod tests { fn recovery_with_invalid_out_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1454,10 +1460,11 @@ mod tests { #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); let heights = [ - TestNetwork::CANOPY_ACTIVATION_HEIGHT - 1, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT + ZIP212_GRACE_PERIOD, + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, ]; let leadbyte_array = [0x02, 0x03, 0x01]; @@ -1493,8 +1500,8 @@ mod tests { fn recovery_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1529,8 +1536,8 @@ mod tests { fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1565,8 +1572,8 @@ mod tests { fn recovery_with_invalid_pk_d() { let mut rng = OsRng; let heights = [ - TestNetwork::SAPLING_ACTIVATION_HEIGHT, - TestNetwork::CANOPY_ACTIVATION_HEIGHT, + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { From c3d89644e2ba8445be66f57bfb62c11151f4ad95 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 12:47:35 +0800 Subject: [PATCH 125/210] Revert SaplingNoteEncryption::new() API to take rng instead of esk --- zcash_client_backend/src/welding_rig.rs | 3 +-- zcash_client_sqlite/src/lib.rs | 16 +++++++++------- zcash_primitives/src/note_encryption.rs | 16 +++++++--------- zcash_primitives/src/transaction/builder.rs | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index b37ee13f0a..fd75031539 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -262,13 +262,12 @@ mod tests { value: value.into(), rseed, }; - let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), to.clone(), Memo::default(), - esk, + &mut rng, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned(); let mut epk = vec![]; diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 655ae89e99..4eb6dcf49d 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -133,13 +133,12 @@ mod tests { value: value.into(), rseed, }; - let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), to.clone(), Memo::default(), - esk, + &mut rng, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; @@ -195,9 +194,13 @@ mod tests { value: value.into(), rseed, }; - let esk = note.generate_or_derive_esk(&mut rng); - let encryptor = - SaplingNoteEncryption::new(extfvk.fvk.ovk, note.clone(), to, Memo::default(), esk); + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + to, + Memo::default(), + &mut rng, + ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; encryptor.epk().write(&mut epk).unwrap(); @@ -220,13 +223,12 @@ mod tests { value: (in_value - value).into(), rseed, }; - let esk = note.generate_or_derive_esk(&mut rng); let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, note.clone(), change_addr, Memo::default(), - esk, + &mut rng, ); let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); let mut epk = vec![]; diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 7a94fc66a5..ee0f996669 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -15,6 +15,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr}; +use rand_core::{CryptoRng, RngCore}; use std::convert::TryInto; use std::fmt; use std::str; @@ -236,8 +237,7 @@ fn prf_ock( /// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap(); /// let cmu = note.cm(&JUBJUB); /// -/// let esk = note.generate_or_derive_esk(&mut rng); -/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), esk); +/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); /// let encCiphertext = enc.encrypt_note_plaintext(); /// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu); /// ``` @@ -252,13 +252,14 @@ pub struct SaplingNoteEncryption { impl SaplingNoteEncryption { /// Creates a new encryption context for the given note. - pub fn new( + pub fn new( ovk: OutgoingViewingKey, note: Note, to: PaymentAddress, memo: Memo, - esk: Fs, + rng: &mut R, ) -> SaplingNoteEncryption { + let esk = note.generate_or_derive_esk(rng); let epk = note.g_d.mul(esk, &JUBJUB); SaplingNoteEncryption { @@ -809,8 +810,7 @@ mod tests { let cmu = note.cm(&JUBJUB); let ovk = OutgoingViewingKey([0; 32]); - let esk = note.generate_or_derive_esk(&mut rng); - let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), esk); + let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), &mut rng); let epk = ne.epk(); let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); @@ -1705,9 +1705,7 @@ mod tests { // Test encryption // - let _esk = note.generate_or_derive_esk(&mut OsRng); - - let mut ne = SaplingNoteEncryption::new(ovk, note, to, Memo(tv.memo), _esk); + let mut ne = SaplingNoteEncryption::new(ovk, note, to, Memo(tv.memo), &mut OsRng); // Swap in the ephemeral keypair from the test vectors ne.esk = esk; ne.epk = epk; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9ace406560..d17b194bd5 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -133,7 +133,7 @@ impl SaplingOutput { self.note.clone(), self.to.clone(), self.memo, - self.note.generate_or_derive_esk(rng), + rng, ); let (zkproof, cv) = prover.output_proof( From 878646855c36cded6a32eb47bcb711ca000289fa Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 12:57:48 +0800 Subject: [PATCH 126/210] Switch on leadbyte instead of tx height when decrypting outputs --- zcash_primitives/src/note_encryption.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index ee0f996669..6dcf25173c 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -362,11 +362,11 @@ fn parse_note_plaintext_without_memo( let mut r = [0u8; 32]; r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); - let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { - Rseed::AfterZip212(r) - } else { + let rseed = if plaintext[0] == 0x01 { let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; Rseed::BeforeZip212(rcm) + } else { + Rseed::AfterZip212(r) }; let diversifier = Diversifier(d); @@ -553,11 +553,11 @@ pub fn try_sapling_output_recovery( let mut r = [0u8; 32]; r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); - let rseed = if P::is_nu_active(NetworkUpgrade::Canopy, height) { - Rseed::AfterZip212(r) - } else { + let rseed = if plaintext[0] == 0x01 { let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; Rseed::BeforeZip212(rcm) + } else { + Rseed::AfterZip212(r) }; let mut memo = [0u8; 512]; From 8968547981f823734c93537764d59c0732636b52 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 13:13:49 +0800 Subject: [PATCH 127/210] Document pub enum Rseed with link to ZIP 212 --- zcash_primitives/src/primitives.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 4f635a867c..1c81fc03b5 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -223,6 +223,11 @@ impl PaymentAddress { } } +/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212). +/// +/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value. +/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive +/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`. #[derive(Copy, Clone, Debug)] pub enum Rseed { BeforeZip212(Fs), From 40a908e0a8d3f5cda0768d99409807fdf8cd5863 Mon Sep 17 00:00:00 2001 From: ying tong Date: Thu, 6 Aug 2020 17:19:15 +0800 Subject: [PATCH 128/210] Refactor leadbyte_array in note_encryption tests Co-authored-by: str4d --- zcash_primitives/src/note_encryption.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 6dcf25173c..eff786188e 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1019,9 +1019,9 @@ mod tests { canopy_activation_height, canopy_activation_height + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03, 0x01]; + let leadbytes = [0x02, 0x03, 0x01]; - for (i, &height) in heights.iter().enumerate() { + for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1032,7 +1032,7 @@ mod tests { &epk, &mut enc_ciphertext, &out_ciphertext, - |pt| pt[0] = leadbyte_array[i], + |pt| pt[0] = leadbyte, ); assert_eq!( try_sapling_note_decryption::( @@ -1196,9 +1196,9 @@ mod tests { canopy_activation_height, canopy_activation_height + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03, 0x01]; + let leadbytes = [0x02, 0x03, 0x01]; - for (i, &height) in heights.iter().enumerate() { + for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1209,7 +1209,7 @@ mod tests { &epk, &mut enc_ciphertext, &out_ciphertext, - |pt| pt[0] = leadbyte_array[i], + |pt| pt[0] = leadbyte, ); assert_eq!( try_sapling_compact_note_decryption::( @@ -1466,9 +1466,9 @@ mod tests { canopy_activation_height, canopy_activation_height + ZIP212_GRACE_PERIOD, ]; - let leadbyte_array = [0x02, 0x03, 0x01]; + let leadbytes = [0x02, 0x03, 0x01]; - for (i, &height) in heights.iter().enumerate() { + for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1479,7 +1479,7 @@ mod tests { &epk, &mut enc_ciphertext, &out_ciphertext, - |pt| pt[0] = leadbyte_array[i], + |pt| pt[0] = leadbyte, ); assert_eq!( try_sapling_output_recovery::( From 0f8f1b3f5d6c58c00dd28b96ef3e3be889a9675b Mon Sep 17 00:00:00 2001 From: ying tong Date: Thu, 6 Aug 2020 17:25:19 +0800 Subject: [PATCH 129/210] Fix parsing of rseed in parse_note_plaintext_without_memo() and try_sapling_output_recovery() Co-authored-by: str4d --- zcash_primitives/src/note_encryption.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index eff786188e..af48c38cb3 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -359,11 +359,12 @@ fn parse_note_plaintext_without_memo( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut r = [0u8; 32]; - r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); + let r: [u8; 32] = plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"); let rseed = if plaintext[0] == 0x01 { - let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; + let rcm = Fs::from_repr(FsRepr(r))?; Rseed::BeforeZip212(rcm) } else { Rseed::AfterZip212(r) @@ -550,11 +551,12 @@ pub fn try_sapling_output_recovery( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut r = [0u8; 32]; - r.copy_from_slice(&plaintext[20..COMPACT_NOTE_SIZE]); + let r: [u8; 32] = plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"); let rseed = if plaintext[0] == 0x01 { - let rcm = Fs::from_repr(FsRepr(r.try_into().expect("slice is the correct length")))?; + let rcm = Fs::from_repr(FsRepr(r))?; Rseed::BeforeZip212(rcm) } else { Rseed::AfterZip212(r) From 7cee29bbccabb5451595e97f701b6796acd75430 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 17:33:33 +0800 Subject: [PATCH 130/210] Use type for Builder impl --- zcash_primitives/src/transaction/builder.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index d17b194bd5..9287442767 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -14,7 +14,6 @@ use std::marker::PhantomData; use crate::{ consensus, - consensus::{MainNetwork, TestNetwork}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -319,22 +318,7 @@ pub struct Builder { phantom: PhantomData

, } -impl Builder { - /// Creates a new `Builder` targeted for inclusion in the block with the given height, - /// using default values for general transaction fields and the default OS random. - /// - /// # Default values - /// - /// The expiry height will be set to the given height plus the default transaction - /// expiry delta (20 blocks). - /// - /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new(height: u32) -> Self { - Builder::new_with_rng(height, OsRng) - } -} - -impl Builder { +impl Builder { /// Creates a new `Builder` targeted for inclusion in the block with the given height, /// using default values for general transaction fields and the default OS random. /// From 5480a376dfa823d3331d3964d36590afcb6e889e Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 18:07:15 +0800 Subject: [PATCH 131/210] Extract derive_esk() into separate function --- zcash_primitives/src/primitives.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 1c81fc03b5..bb5b2e1c90 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -346,7 +346,17 @@ impl Note { // reduce to uniform value E::Fs::to_uniform(&buffer[..]) } - Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()), + Rseed::AfterZip212(_) => self.derive_esk().unwrap(), + } + } + + /// Returns the derived `esk` if this note was created after ZIP 212 activated. + pub fn derive_esk(&self) -> Option { + match self.rseed { + Rseed::BeforeZip212(_) => None, + Rseed::AfterZip212(rseed) => { + Some(E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes())) + } } } } From d54fd09c5f736ec0da168beed3bd6f5e54defceb Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Aug 2020 18:18:58 +0800 Subject: [PATCH 132/210] Add esk check in parse_note_plaintext_without_memo() and try_sapling_output_recovery() --- zcash_primitives/src/note_encryption.rs | 33 ++++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index af48c38cb3..cf0b446e07 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -6,7 +6,7 @@ use crate::{ jubjub::{ edwards, fs::{Fs, FsRepr}, - PrimeOrder, ToUniform, Unknown, + PrimeOrder, Unknown, }, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; @@ -20,10 +20,7 @@ use std::convert::TryInto; use std::fmt; use std::str; -use crate::{ - keys::{prf_expand, OutgoingViewingKey}, - JUBJUB, -}; +use crate::{keys::OutgoingViewingKey, JUBJUB}; pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; @@ -346,6 +343,7 @@ impl SaplingNoteEncryption { fn parse_note_plaintext_without_memo( height: u32, ivk: &Fs, + epk: &edwards::Point, cmu: &Fr, plaintext: &[u8], ) -> Option<(Note, PaymentAddress)> { @@ -383,6 +381,13 @@ fn parse_note_plaintext_without_memo( return None; } + if let Rseed::AfterZip212(_) = note.rseed { + let derived_esk = note.derive_esk().unwrap(); + if note.g_d.mul(derived_esk, &JUBJUB) != *epk { + return None; + } + } + Some((note, to)) } @@ -440,15 +445,7 @@ pub fn try_sapling_note_decryption( NOTE_PLAINTEXT_SIZE ); - let (note, to) = parse_note_plaintext_without_memo::

(height, ivk, cmu, &plaintext)?; - - match note.rseed { - Rseed::AfterZip212(rseed) => { - let derived_esk = Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()); - assert_eq!(note.g_d.mul(derived_esk, &JUBJUB), *epk); - } - _ => (), - } + let (note, to) = parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext)?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -482,7 +479,7 @@ pub fn try_sapling_compact_note_decryption( plaintext.copy_from_slice(&enc_ciphertext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); - parse_note_plaintext_without_memo::

(height, ivk, cmu, &plaintext) + parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext) } /// Recovery of the full note plaintext by the sender. @@ -583,6 +580,12 @@ pub fn try_sapling_output_recovery( return None; } + if let Rseed::AfterZip212(_) = note.rseed { + if note.derive_esk().unwrap() != esk { + return None; + } + } + Some((note, to, Memo(memo))) } From 13f4d0844e30159f262eef66cb73744c94c421ec Mon Sep 17 00:00:00 2001 From: ying tong Date: Fri, 7 Aug 2020 00:12:07 +0800 Subject: [PATCH 133/210] Avoid using unwrap() when calling derive_esk() Co-authored-by: Daira Hopwood --- zcash_primitives/src/note_encryption.rs | 9 ++++----- zcash_primitives/src/primitives.rs | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index cf0b446e07..e6a64f0610 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -381,8 +381,7 @@ fn parse_note_plaintext_without_memo( return None; } - if let Rseed::AfterZip212(_) = note.rseed { - let derived_esk = note.derive_esk().unwrap(); + if let Some(derived_esk) = note.derive_esk() { if note.g_d.mul(derived_esk, &JUBJUB) != *epk { return None; } @@ -461,7 +460,7 @@ pub fn try_sapling_note_decryption( /// /// Implements the procedure specified in [`ZIP 307`]. /// -/// [`ZIP 307`]: https://github.com/zcash/zips/pull/226 +/// [`ZIP 307`]: https://zips.z.cash/zip-0307 pub fn try_sapling_compact_note_decryption( height: u32, ivk: &Fs, @@ -580,8 +579,8 @@ pub fn try_sapling_output_recovery( return None; } - if let Rseed::AfterZip212(_) = note.rseed { - if note.derive_esk().unwrap() != esk { + if let Some(derived_esk) = note.derive_esk() { + if derived_esk != esk { return None; } } diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index bb5b2e1c90..e475bd7ced 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -337,8 +337,8 @@ impl Note { } pub fn generate_or_derive_esk(&self, rng: &mut R) -> E::Fs { - match self.rseed { - Rseed::BeforeZip212(_) => { + match self.derive_esk() { + None => { // create random 64 byte buffer let mut buffer = [0u8; 64]; &rng.fill_bytes(&mut buffer); @@ -346,7 +346,7 @@ impl Note { // reduce to uniform value E::Fs::to_uniform(&buffer[..]) } - Rseed::AfterZip212(_) => self.derive_esk().unwrap(), + Some(esk) => esk, } } From 72cc8fc9165ad5593982242c7667907db3f09d02 Mon Sep 17 00:00:00 2001 From: ying tong Date: Fri, 7 Aug 2020 08:46:15 +0800 Subject: [PATCH 134/210] Minor refactor of enumeration in tests in note_encryption.rs Co-authored-by: str4d --- zcash_primitives/src/note_encryption.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index e6a64f0610..bdd6ee1191 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1025,7 +1025,7 @@ mod tests { ]; let leadbytes = [0x02, 0x03, 0x01]; - for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1202,7 +1202,7 @@ mod tests { ]; let leadbytes = [0x02, 0x03, 0x01]; - for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); @@ -1472,7 +1472,7 @@ mod tests { ]; let leadbytes = [0x02, 0x03, 0x01]; - for (_i, (&height, &leadbyte)) in heights.iter().zip(leadbytes.iter()).enumerate() { + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); From e22e15a34aa1df36c9f3c4980663c15eb724a437 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 7 Aug 2020 16:36:46 +0100 Subject: [PATCH 135/210] CI: Build zcash_proofs against WASM targets This ensures that we don't introduce any regressions for WASM consumers. --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3735bf199c..203e02a0a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,31 @@ jobs: command: test args: --verbose --release --all -- --ignored + build: + name: Build target ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + matrix: + target: + - wasm32-unknown-unknown + - wasm32-wasi + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + override: true + - name: Add target + run: rustup target add ${{ matrix.target }} + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build zcash_proofs for target + working-directory: ./zcash_proofs + run: cargo build --verbose --no-default-features --target ${{ matrix.target }} + codecov: name: Code coverage runs-on: ubuntu-latest From 663d4ee4c4fbc1a2ee2d1ef22e047387a12e9851 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 20 Nov 2019 15:10:15 +0000 Subject: [PATCH 136/210] CI: Measure code coverage for zkcrypto crates --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3735bf199c..65789eb9b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,7 +105,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: tarpaulin - args: --release --timeout 600 --out Xml --packages "zcash_client_backend,zcash_primitives,zcash_proofs" + args: --release --timeout 600 --out Xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v1.0.3 with: From 94c22ed2cb24ba8281242bd974dd0ce62c45ff2e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 07:21:22 +0100 Subject: [PATCH 137/210] CI: Fetch Zcash params for code coverage --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65789eb9b7..9976eb2abe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,21 @@ jobs: with: command: install args: cargo-tarpaulin + + - name: Fetch path to Zcash parameters + working-directory: ./zcash_proofs + run: echo "::set-env name=ZCASH_PARAMS::$(cargo run --release --example get-params-path --features directories)" + - name: Cache Zcash parameters + id: cache-params + uses: actions/cache@v2 + with: + path: ${{ env.ZCASH_PARAMS }} + key: ${{ runner.os }}-params + - name: Fetch Zcash parameters + if: steps.cache-params.outputs.cache-hit != 'true' + working-directory: ./zcash_proofs + run: cargo run --release --example download-params --features download-params + - name: Generate coverage report uses: actions-rs/cargo@v1 with: From 1e8fd4da685c8e91c8d727c887d5f861bc27282b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 08:02:29 +0100 Subject: [PATCH 138/210] group: CofactorGroup::mul_by_cofactor -> CofactorGroup::clear_cofactor The generic API now only guarantees that the torsion component is cleared deterministically; group elements may be multiplied by multiples of the cofactor (not necessarily the actual cofactor), as long as the choice of multiplier is fixed for a given implementation. --- bellman/src/groth16/tests/dummy_engine.rs | 2 +- group/src/cofactor.rs | 12 +++++++++--- pairing/src/bls12_381/ec.rs | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 8f11fe1549..c4a7ca9eee 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -399,7 +399,7 @@ impl PrimeGroup for Fr {} impl CofactorGroup for Fr { type Subgroup = Fr; - fn mul_by_cofactor(&self) -> Self::Subgroup { + fn clear_cofactor(&self) -> Self::Subgroup { *self } diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs index 9ccc983940..ba52c42741 100644 --- a/group/src/cofactor.rs +++ b/group/src/cofactor.rs @@ -17,10 +17,16 @@ pub trait CofactorGroup: /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. type Subgroup: PrimeGroup + Into; - /// Returns `[h] self`, where `h` is the cofactor of the group. + /// Maps `self` to the prime-order subgroup by clearing any torsion component. + /// + /// This function computes `[k.h] self`; that is, this function multiplies `self` by a + /// multiple of the cofactor (not necessarily the actual cofactor). + /// + /// This function is deterministic: `k` is fixed for a given implementation, and the + /// map defined by this function is opaque, but well-defined. /// /// If `Self` implements [`PrimeGroup`], this returns `self`. - fn mul_by_cofactor(&self) -> Self::Subgroup; + fn clear_cofactor(&self) -> Self::Subgroup; /// Returns `self` if it is contained in the prime-order subgroup. /// @@ -33,7 +39,7 @@ pub trait CofactorGroup: /// - `true` if `self` is in the torsion subgroup. /// - `false` if `self` is not in the torsion subgroup. fn is_small_order(&self) -> Choice { - self.mul_by_cofactor().is_identity() + self.clear_cofactor().is_identity() } /// Determines if this element is "torsion free", i.e., is contained in the diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 3af8dc5e9c..ed0efd63fd 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -1024,7 +1024,9 @@ macro_rules! curve_impl { impl CofactorGroup for $projective { type Subgroup = $subgroup; - fn mul_by_cofactor(&self) -> Self::Subgroup { + fn clear_cofactor(&self) -> Self::Subgroup { + // This implementation uses the cofactor directly, and differs from the + // bls12_381 crate which uses a multiple of the cofactor. $subgroup($affine::from(*self).scale_by_cofactor().into()) } From ed6b7eceefe5ff66c5fe412bd49b45f4f90a5014 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 08:12:48 +0100 Subject: [PATCH 139/210] group: Hard-code a w-NAF window size of 4 We were already ignoring the actual bit length of the scalar, and selecting the window size based on the maximum bit length, which effectively hard-coded a window size of 4. --- bellman/src/groth16/tests/dummy_engine.rs | 4 ---- group/src/wnaf.rs | 8 ++----- pairing/src/bls12_381/ec.rs | 26 ----------------------- 3 files changed, 2 insertions(+), 36 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index c4a7ca9eee..4a3d6da6b2 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -417,10 +417,6 @@ impl Curve for Fr { } impl WnafGroup for Fr { - fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { - 3 - } - fn recommended_wnaf_for_num_scalars(_: usize) -> usize { 3 } diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 20651d4379..e81adea2d3 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -6,10 +6,6 @@ use super::Group; /// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`]. pub trait WnafGroup: Group { - /// Recommends a wNAF window table size given a scalar. Always returns a number - /// between 2 and 22, inclusive. - fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> usize; - /// Recommends a wNAF window size given the number of scalars you intend to multiply /// a base by. Always returns a number between 2 and 22, inclusive. fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; @@ -154,8 +150,8 @@ impl Wnaf<(), Vec, Vec> { /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform /// exponentiations with `.base(..)`. pub fn scalar(&mut self, scalar: &::Scalar) -> Wnaf, &[i64]> { - // Compute the appropriate window size for the scalar. - let window_size = G::recommended_wnaf_for_scalar(&scalar); + // We hard-code a window size of 4. + let window_size = 4; // Compute the wNAF form of the scalar. wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index ed0efd63fd..453b7497aa 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -1097,12 +1097,6 @@ macro_rules! curve_impl { } impl WnafGroup for $projective { - fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { - Self::empirical_recommended_wnaf_for_scalar( - ::NUM_BITS as usize, - ) - } - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) } @@ -1454,16 +1448,6 @@ pub mod g1 { } impl G1 { - fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { - if num_bits >= 130 { - 4 - } else if num_bits >= 34 { - 3 - } else { - 2 - } - } - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { const RECOMMENDATIONS: [usize; 12] = [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; @@ -2086,16 +2070,6 @@ pub mod g2 { } impl G2 { - fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { - if num_bits >= 103 { - 4 - } else if num_bits >= 37 { - 3 - } else { - 2 - } - } - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; From 418cba0e85cc62e239af7b5123c1d6ff58402ba9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 08:16:44 +0100 Subject: [PATCH 140/210] group: Document that Group::random is non-deterministic --- group/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/group/src/lib.rs b/group/src/lib.rs index 57914f8207..1ac5e31e60 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -62,7 +62,9 @@ pub trait Group: /// Scalars modulo the order of this group's scalar field. type Scalar: PrimeField; - /// Returns an element chosen uniformly at random using a user-provided RNG. + /// Returns an element chosen uniformly at random from this group. + /// + /// This function is non-deterministic, and samples from the user-provided RNG. fn random(rng: &mut R) -> Self; /// Returns the additive identity, also known as the "neutral element". From a5a6f57c5abcf47387d1c9e1ea2ee8ab01925734 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 18:25:52 +0100 Subject: [PATCH 141/210] Squashed 'bls12_381/' changes from 1a2e9f3..d0ea5d4 d0ea5d4 Merge pull request #32 from narodnik/sum 24aa1a4 Merge pull request #31 from zkcrypto/release-0.1.1 fb7c4cb add cargo fmt for sum traits (code we added) ccef392 add sum iterator implementations 82e14ed Release 0.1.1 a3608d4 Put endo optimizations behind endo crate feature. e32494e Merge pull request #18 from mmaker/master 948b199 Fix typo in comment. b3d1fe1 Merge pull request #27 from rex4539/fix-typos 253f681 Merge pull request #25 from mmaker/fix/sage-script c55f88f Fix typos 14b5e16 No need to define a polynomial ring in notes/design.rs. c9d17f6 Make sage script in notes/design.rs work with sage 3.9. af9ec4d Minor changes to comments documenting `clear_cofactor` 7dc6f31 Add clear_cofactor. git-subtree-dir: bls12_381 git-subtree-split: d0ea5d4958cae999dea1800207704171aa07a9ef --- .github/workflows/ci.yml | 12 +- Cargo.toml | 5 +- README.md | 5 +- RELEASES.md | 9 ++ src/g1.rs | 106 +++++++++++++ src/g2.rs | 325 ++++++++++++++++++++++++++++++++++++++- src/notes/design.rs | 9 +- src/scalar.rs | 2 +- 8 files changed, 462 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39066dbfd9..1388989f0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,8 +43,18 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --verbose --release --tests + args: --verbose --release --tests --features endo - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release --features endo + - name: Build tests (no endomorphism) + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --tests + - name: Run tests (no endomorphism) uses: actions-rs/cargo@v1 with: command: test diff --git a/Cargo.toml b/Cargo.toml index 0bfb0d443d..b4354ef8f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://github.com/zkcrypto/bls12_381" license = "MIT/Apache-2.0" name = "bls12_381" repository = "https://github.com/zkcrypto/bls12_381" -version = "0.1.0" +version = "0.1.1" edition = "2018" [package.metadata.docs.rs] @@ -30,3 +30,6 @@ groups = [] pairings = ["groups"] alloc = [] nightly = ["subtle/nightly"] + +# GLV patents US7110538B2 and US7995752B2 expire in September 2020. +endo = [] diff --git a/README.md b/README.md index ba61f300ba..70a238e50a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This crate provides an implementation of the BLS12-381 pairing-friendly elliptic * `pairings` (on by default): Enables some APIs for performing pairings. * `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. * `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. +* `endo`: Enables optimizations that leverage curve endomorphisms, which may run foul of patents US7110538B2 and US7995752B2 set to expire in September 2020. ## [Documentation](https://docs.rs/bls12_381) @@ -26,7 +27,7 @@ BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family * q = z4 - z2 + 1 * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` -... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extention field Fp2 is defined as Fp(u) / (u2 + 1). +... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extension field Fp2 is defined as Fp(u) / (u2 + 1). BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. @@ -34,7 +35,7 @@ BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing per Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. -In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. +In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yield a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) diff --git a/RELEASES.md b/RELEASES.md index 69afd5204d..85fcd4ae5a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +# 0.1.1 + +Added `clear_cofactor` methods to `G1Projective` and `G2Projective`. If the crate feature `endo` +is enabled the G2 cofactor clearing will use the curve endomorphism technique described by +[Budroni-Pintore](https://ia.cr/2017/419). If the crate feature `endo` is _not_ enabled then +the code will simulate the effects of the Budroni-Pintore cofactor clearing in order to keep +the API consistent. In September 2020, when patents US7110538B2 and US7995752B2 expire, the +endo feature will be made default. However, for now it must be explicitly enabled. + # 0.1.0 Initial release. diff --git a/src/g1.rs b/src/g1.rs index aa90dc16b8..0ab1e1d01b 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. +use core::borrow::Borrow; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -140,6 +142,18 @@ impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { } } +impl Sum for G1Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + impl_binops_additive!(G1Projective, G1Affine); impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); @@ -735,6 +749,34 @@ impl G1Projective { acc } + /// Multiply `self` by `crate::BLS_X`, using double and add. + fn mul_by_x(&self) -> G1Projective { + let mut xself = G1Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = crate::BLS_X >> 1; + let mut tmp = *self; + while x != 0 { + tmp = tmp.double(); + + if x % 2 == 1 { + xself += tmp; + } + x >>= 1; + } + // finally, flip the sign + if crate::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Multiplies by $(1 - z)$, where $z$ is the parameter of BLS12-381, which + /// [suffices to clear](https://ia.cr/2019/403) the cofactor and map + /// elliptic curve points to elements of $\mathbb{G}\_1$. + pub fn clear_cofactor(&self) -> G1Projective { + self - self.mul_by_x() + } + /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This /// function will panic if `p.len() != q.len()`. pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { @@ -1303,6 +1345,70 @@ fn test_is_torsion_free() { assert!(bool::from(G1Affine::generator().is_torsion_free())); } +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + let generator = G1Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -Scalar::from(crate::BLS_X) + } else { + Scalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G1Projective::generator() * Scalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[test] +fn test_clear_cofactor() { + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G1Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G1Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + let point = G1Projective { + x: Fp::from_raw_unchecked([ + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c, + ]), + y: Fp::from_raw_unchecked([ + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b, + ]), + z: Fp::from_raw_unchecked([ + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791, + ]), + }; + + assert!(bool::from(point.is_on_curve())); + assert!(!bool::from(G1Affine::from(point).is_torsion_free())); + let cleared_point = point.clear_cofactor(); + assert!(bool::from(cleared_point.is_on_curve())); + assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free())); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + let h_eff = Scalar::from(1) + Scalar::from(crate::BLS_X); + assert_eq!(point.clear_cofactor(), point * h_eff); +} + #[test] fn test_batch_normalize() { let a = G1Projective::generator().double(); diff --git a/src/g2.rs b/src/g2.rs index 136cd03db5..8d85d72e92 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. +use core::borrow::Borrow; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -141,6 +143,18 @@ impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { } } +impl Sum for G2Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + impl_binops_additive!(G2Projective, G2Affine); impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); @@ -805,7 +819,7 @@ impl G2Projective { G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) } - fn multiply(&self, by: &[u8; 32]) -> G2Projective { + fn multiply(&self, by: &[u8]) -> G2Projective { let mut acc = G2Projective::identity(); // This is a simple double-and-add implementation of point @@ -827,6 +841,132 @@ impl G2Projective { acc } + #[cfg(feature = "endo")] + fn psi(&self) -> G2Projective { + // 1 / ((u+1) ^ ((q-1)/3)) + let psi_coeff_x = Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + }; + // 1 / ((u+1) ^ (p-1)/2) + let psi_coeff_y = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8, + ]), + c1: Fp::from_raw_unchecked([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2, + ]), + }; + + G2Projective { + // x = frobenius(x)/((u+1)^((p-1)/3)) + x: self.x.frobenius_map() * psi_coeff_x, + // y = frobenius(y)/(u+1)^((p-1)/2) + y: self.y.frobenius_map() * psi_coeff_y, + // z = frobenius(z) + z: self.z.frobenius_map(), + } + } + + #[cfg(feature = "endo")] + fn psi2(&self) -> G2Projective { + // 1 / 2 ^ ((q-1)/3) + let psi2_coeff_x = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fp::zero(), + }; + + G2Projective { + // x = frobenius^2(x)/2^((p-1)/3) + x: self.x.frobenius_map().frobenius_map() * psi2_coeff_x, + // y = -frobenius^2(y) + y: self.y.frobenius_map().frobenius_map().neg(), + // z = z + z: self.z, + } + } + + /// Multiply `self` by `crate::BLS_X`, using double and add. + #[cfg(feature = "endo")] + fn mul_by_x(&self) -> G2Projective { + let mut xself = G2Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = crate::BLS_X >> 1; + let mut acc = *self; + while x != 0 { + acc = acc.double(); + if x % 2 == 1 { + xself += acc; + } + x >>= 1; + } + // finally, flip the sign + if crate::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). + /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot + /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the + /// parameter of BLS12-381. + /// + /// The endomorphism is only actually used if the crate feature `endo` is + /// enabled, and it is disabled by default to mitigate potential patent + /// issues. + pub fn clear_cofactor(&self) -> G2Projective { + #[cfg(feature = "endo")] + fn clear_cofactor(this: &G2Projective) -> G2Projective { + let t1 = this.mul_by_x(); // [x] P + let t2 = this.psi(); // psi(P) + + this.double().psi2() // psi^2(2P) + + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - this // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } + + #[cfg(not(feature = "endo"))] + fn clear_cofactor(this: &G2Projective) -> G2Projective { + this.multiply(&[ + 0x51, 0x55, 0xa9, 0xaa, 0x5, 0x0, 0x2, 0xe8, 0xb4, 0xf6, 0xbb, 0xde, 0xa, 0x4c, + 0x89, 0x59, 0xa3, 0xf6, 0x89, 0x66, 0xc0, 0xcb, 0x54, 0xe9, 0x1a, 0x7c, 0x47, 0xd7, + 0x69, 0xec, 0xc0, 0x2e, 0xb0, 0x12, 0x12, 0x5d, 0x1, 0xbf, 0x82, 0x6d, 0x95, 0xdb, + 0x31, 0x87, 0x17, 0x2f, 0x9c, 0x32, 0xe1, 0xff, 0x8, 0x15, 0x3, 0xff, 0x86, 0x99, + 0x68, 0xd7, 0x5a, 0x14, 0xe9, 0xa8, 0xe2, 0x88, 0x28, 0x35, 0x1b, 0xa9, 0xe, 0x6a, + 0x4c, 0x58, 0xb3, 0x75, 0xee, 0xf2, 0x8, 0x9f, 0xc6, 0xb, + ]) + } + + clear_cofactor(self) + } + /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This /// function will panic if `p.len() != q.len()`. pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { @@ -1551,6 +1691,189 @@ fn test_is_torsion_free() { assert!(bool::from(G2Affine::generator().is_torsion_free())); } +#[cfg(feature = "endo")] +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + let generator = G2Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -Scalar::from(crate::BLS_X) + } else { + Scalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G2Projective::generator() * Scalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[cfg(feature = "endo")] +#[test] +fn test_psi() { + let generator = G2Projective::generator(); + + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + assert!(bool::from(point.is_on_curve())); + + // psi2(P) = psi(psi(P)) + assert_eq!(generator.psi2(), generator.psi().psi()); + assert_eq!(point.psi2(), point.psi().psi()); + // psi(P) is a morphism + assert_eq!(generator.double().psi(), generator.psi().double()); + assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); + // psi(P) behaves in the same way on the same projective point + let mut normalized_point = [G2Affine::identity()]; + G2Projective::batch_normalize(&[point], &mut normalized_point); + let normalized_point = G2Projective::from(normalized_point[0]); + assert_eq!(point.psi(), normalized_point.psi()); + assert_eq!(point.psi2(), normalized_point.psi2()); +} + +#[test] +fn test_clear_cofactor() { + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + + assert!(bool::from(point.is_on_curve())); + assert!(!bool::from(G2Affine::from(point).is_torsion_free())); + let cleared_point = point.clear_cofactor(); + + assert!(bool::from(cleared_point.is_on_curve())); + assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G2Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G2Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + let h_eff_modq: [u8; 32] = [ + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); + assert_eq!( + cleared_point.clear_cofactor(), + cleared_point.multiply(&h_eff_modq) + ); +} + #[test] fn test_batch_normalize() { let a = G2Projective::generator().double(); diff --git a/src/notes/design.rs b/src/notes/design.rs index d245260ef6..0f8c7fafbf 100644 --- a/src/notes/design.rs +++ b/src/notes/design.rs @@ -46,10 +46,9 @@ //! y = psqrt(rhs) //! p = ec(x, y) * g1_h(param) //! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g1 generator: %s" % p +//! print("g1 generator: {}".format(p)) //! break -//! Fqx. = PolynomialRing(Fq, 'j') -//! Fq2. = GF(q^2, modulus=j^2 + 1) +//! Fq2. = GF(q^2, modulus=[1, 0, 1]) //! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) //! assert(ec2.order() == (r * g2_h(param))) //! for x in range(0,100): @@ -57,7 +56,7 @@ //! if rhs.is_square(): //! y = psqrt(rhs) //! p = ec2(Fq2(x), y) * g2_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g2 generator: %s" % p +//! if not p.is_zero() and (p * r).is_zero(): +//! print("g2 generator: {}".format(p)) //! break //! ``` diff --git a/src/scalar.rs b/src/scalar.rs index d4a7ab2d26..b3140afdbb 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -256,7 +256,7 @@ impl Scalar { // // and computing their sum in the field. It remains to see that arbitrary 256-bit // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by + // reduction works so long as the product is less than R=2^256 multiplied by // the modulus. This holds because for any `c` smaller than the modulus, we have // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the // reduction always works so long as `c` is in the field; in this case it is either the From 9e9bec826c49caba00ed2aa846ae70a32bc8020c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 13 Aug 2020 00:09:27 +0100 Subject: [PATCH 142/210] group: Fix documentation of CofactorGroup::clear_cofactor Co-authored-by: Sean Bowe --- group/src/cofactor.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs index ba52c42741..4722909350 100644 --- a/group/src/cofactor.rs +++ b/group/src/cofactor.rs @@ -17,13 +17,13 @@ pub trait CofactorGroup: /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. type Subgroup: PrimeGroup + Into; - /// Maps `self` to the prime-order subgroup by clearing any torsion component. + /// Maps `self` to the prime-order subgroup by multiplying this element by some + /// `k`-multiple of the cofactor. /// - /// This function computes `[k.h] self`; that is, this function multiplies `self` by a - /// multiple of the cofactor (not necessarily the actual cofactor). - /// - /// This function is deterministic: `k` is fixed for a given implementation, and the - /// map defined by this function is opaque, but well-defined. + /// The value `k` does not vary between inputs for a given implementation, but may + /// vary between different implementations of `CofactorGroup` because some groups have + /// more efficient methods of clearing the cofactor when `k` is allowed to be + /// different than `1`. /// /// If `Self` implements [`PrimeGroup`], this returns `self`. fn clear_cofactor(&self) -> Self::Subgroup; From 7a2235ad039d0cf158bba444ecf4c74a9192b7c4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 15:51:03 +0100 Subject: [PATCH 143/210] jubjub: Replace Choice::unwrap_u8 with bool::from The latter is clearer and the intended route for un-CT-ing Choices. --- jubjub/src/fr.rs | 36 +++++++++++++----------------------- jubjub/src/lib.rs | 38 +++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs index 827793326a..98c864b7fc 100644 --- a/jubjub/src/fr.rs +++ b/jubjub/src/fr.rs @@ -54,7 +54,7 @@ impl ConstantTimeEq for Fr { impl PartialEq for Fr { #[inline] fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -805,57 +805,47 @@ fn test_from_bytes() { ); // -1 should work - assert!( + assert!(bool::from( Fr::from_bytes(&[ 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 ]) .is_some() - .unwrap_u8() - == 1 - ); + )); // modulus is invalid - assert!( + assert!(bool::from( Fr::from_bytes(&[ 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); // Anything larger than the modulus is invalid - assert!( + assert!(bool::from( Fr::from_bytes(&[ 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); - assert!( + assert!(bool::from( Fr::from_bytes(&[ 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); - assert!( + assert!(bool::from( Fr::from_bytes(&[ 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); } #[test] @@ -1056,7 +1046,7 @@ fn test_squaring() { #[test] fn test_inversion() { - assert_eq!(Fr::zero().invert().is_none().unwrap_u8(), 1); + assert!(bool::from(Fr::zero().invert().is_none())); assert_eq!(Fr::one().invert().unwrap(), Fr::one()); assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); @@ -1113,7 +1103,7 @@ fn test_sqrt() { for _ in 0..100 { let square_root = square.sqrt(); - if square_root.is_none().unwrap_u8() == 1 { + if bool::from(square_root.is_none()) { none_count += 1; } else { assert_eq!(square_root.unwrap() * square_root.unwrap(), square); diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs index 09cb97ee23..da26fd96cf 100644 --- a/jubjub/src/lib.rs +++ b/jubjub/src/lib.rs @@ -77,7 +77,7 @@ impl ConstantTimeEq for AffinePoint { impl PartialEq for AffinePoint { fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -136,7 +136,7 @@ impl ConditionallySelectable for ExtendedPoint { impl PartialEq for ExtendedPoint { fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -907,9 +907,9 @@ fn test_is_on_curve_var() { #[test] fn test_d_is_non_quadratic_residue() { - assert!(EDWARDS_D.sqrt().is_none().unwrap_u8() == 1); - assert!((-EDWARDS_D).sqrt().is_none().unwrap_u8() == 1); - assert!((-EDWARDS_D).invert().unwrap().sqrt().is_none().unwrap_u8() == 1); + assert!(bool::from(EDWARDS_D.sqrt().is_none())); + assert!(bool::from((-EDWARDS_D).sqrt().is_none())); + assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none())); } #[test] @@ -1121,9 +1121,9 @@ const EIGHT_TORSION: [AffinePoint; 8] = [ #[test] fn find_eight_torsion() { let g = ExtendedPoint::from(FULL_GENERATOR); - assert!(g.is_small_order().unwrap_u8() == 0); + assert!(!bool::from(g.is_small_order())); let g = g.multiply(&FR_MODULUS_BYTES); - assert!(g.is_small_order().unwrap_u8() == 1); + assert!(bool::from(g.is_small_order())); let mut cur = g; @@ -1142,22 +1142,22 @@ fn find_curve_generator() { let mut trial_bytes = [0; 32]; for _ in 0..255 { let a = AffinePoint::from_bytes(trial_bytes); - if a.is_some().unwrap_u8() == 1 { + if bool::from(a.is_some()) { let a = a.unwrap(); assert!(a.is_on_curve_vartime()); let b = ExtendedPoint::from(a); let b = b.multiply(&FR_MODULUS_BYTES); - assert!(b.is_small_order().unwrap_u8() == 1); + assert!(bool::from(b.is_small_order())); let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); + assert!(bool::from(b.is_small_order())); let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); - if b.is_identity().unwrap_u8() == 0 { + assert!(bool::from(b.is_small_order())); + if !bool::from(b.is_identity()) { let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); - assert!(b.is_identity().unwrap_u8() == 1); + assert!(bool::from(b.is_small_order())); + assert!(bool::from(b.is_identity())); assert_eq!(FULL_GENERATOR, a); - assert!(a.mul_by_cofactor().is_torsion_free().unwrap_u8() == 1); + assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); return; } } @@ -1171,7 +1171,7 @@ fn find_curve_generator() { #[test] fn test_small_order() { for point in EIGHT_TORSION.iter() { - assert!(point.is_small_order().unwrap_u8() == 1); + assert!(bool::from(point.is_small_order())); } } @@ -1186,11 +1186,11 @@ fn test_is_identity() { assert!(a.v != b.v); assert!(a.z != b.z); - assert!(a.is_identity().unwrap_u8() == 1); - assert!(b.is_identity().unwrap_u8() == 1); + assert!(bool::from(a.is_identity())); + assert!(bool::from(b.is_identity())); for point in EIGHT_TORSION.iter() { - assert!(point.mul_by_cofactor().is_identity().unwrap_u8() == 1); + assert!(bool::from(point.mul_by_cofactor().is_identity())); } } From a6f2172b2090b2de32731040eaac6c53d8159af2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 15:56:27 +0100 Subject: [PATCH 144/210] bls12_381: Replace Choice::unwrap_u8 with bool::from The latter is clearer and the intended route for un-CT-ing Choices. --- bls12_381/src/fp.rs | 14 ++++++-------- bls12_381/src/fp2.rs | 6 +++--- bls12_381/src/scalar.rs | 36 +++++++++++++----------------------- 3 files changed, 22 insertions(+), 34 deletions(-) diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs index 02d1d78558..dfe8849953 100644 --- a/bls12_381/src/fp.rs +++ b/bls12_381/src/fp.rs @@ -47,7 +47,7 @@ impl Eq for Fp {} impl PartialEq for Fp { #[inline] fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -566,7 +566,7 @@ fn test_equality() { let eq = a == b; let ct_eq = a.ct_eq(&b); - assert_eq!(eq, ct_eq.unwrap_u8() == 1); + assert_eq!(eq, bool::from(ct_eq)); eq } @@ -762,18 +762,16 @@ fn test_from_bytes() { .unwrap() ); - assert!( + assert!(bool::from( Fp::from_bytes(&[ 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); - assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1); + assert!(bool::from(Fp::from_bytes(&[0xff; 48]).is_none())); } #[test] @@ -823,7 +821,7 @@ fn test_inversion() { ]); assert_eq!(a.invert().unwrap(), b); - assert!(Fp::zero().invert().is_none().unwrap_u8() == 1); + assert!(bool::from(Fp::zero().invert().is_none())); } #[test] diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs index 3890d31b3d..1f5370845b 100644 --- a/bls12_381/src/fp2.rs +++ b/bls12_381/src/fp2.rs @@ -44,7 +44,7 @@ impl Eq for Fp2 {} impl PartialEq for Fp2 { #[inline] fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -361,7 +361,7 @@ fn test_equality() { let eq = a == b; let ct_eq = a.ct_eq(&b); - assert_eq!(eq, ct_eq.unwrap_u8() == 1); + assert_eq!(eq, bool::from(ct_eq)); eq } @@ -788,7 +788,7 @@ fn test_inversion() { assert_eq!(a.invert().unwrap(), b); - assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1); + assert!(bool::from(Fp2::zero().invert().is_none())); } #[test] diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs index 5bbfec5c4a..c9c385d2df 100644 --- a/bls12_381/src/scalar.rs +++ b/bls12_381/src/scalar.rs @@ -54,7 +54,7 @@ impl ConstantTimeEq for Scalar { impl PartialEq for Scalar { #[inline] fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 + bool::from(self.ct_eq(other)) } } @@ -834,55 +834,45 @@ fn test_from_bytes() { ); // -1 should work - assert!( + assert!(bool::from( Scalar::from_bytes(&[ 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 ]) .is_some() - .unwrap_u8() - == 1 - ); + )); // modulus is invalid - assert!( + assert!(bool::from( Scalar::from_bytes(&[ 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); // Anything larger than the modulus is invalid - assert!( + assert!(bool::from( Scalar::from_bytes(&[ 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 ]) .is_none() - .unwrap_u8() - == 1 - ); - assert!( + )); + assert!(bool::from( Scalar::from_bytes(&[ 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 ]) .is_none() - .unwrap_u8() - == 1 - ); - assert!( + )); + assert!(bool::from( Scalar::from_bytes(&[ 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 ]) .is_none() - .unwrap_u8() - == 1 - ); + )); } #[test] @@ -1083,7 +1073,7 @@ fn test_squaring() { #[test] fn test_inversion() { - assert_eq!(Scalar::zero().invert().is_none().unwrap_u8(), 1); + assert!(bool::from(Scalar::zero().invert().is_none())); assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); @@ -1143,7 +1133,7 @@ fn test_sqrt() { for _ in 0..100 { let square_root = square.sqrt(); - if square_root.is_none().unwrap_u8() == 1 { + if bool::from(square_root.is_none()) { none_count += 1; } else { assert_eq!(square_root.unwrap() * square_root.unwrap(), square); From 7eaf6493e840e50d5062a9ed331104d921e7c636 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 13 Aug 2020 18:11:44 +0100 Subject: [PATCH 145/210] pairing: Require G1 and G2 to be PrimeCurve Pairings require that G1, G2, and GT are groups of prime order. --- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/mod.rs | 2 +- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 19 +- bellman/src/groth16/verifier.rs | 2 +- bellman/src/multiexp.rs | 30 +-- group/src/tests/mod.rs | 20 +- pairing/src/bls12_381/ec.rs | 285 +--------------------- pairing/src/bls12_381/mod.rs | 2 +- pairing/src/bls12_381/tests/mod.rs | 9 +- pairing/src/lib.rs | 8 +- pairing/src/tests/engine.rs | 2 +- 12 files changed, 46 insertions(+), 337 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index d04ed6c0a0..b78efcec3d 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf, WnafGroup}; +use group::{prime::PrimeCurveAffine, Curve, Group, Wnaf, WnafGroup}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 624324f2af..41859fbd2e 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{cofactor::CofactorCurveAffine, GroupEncoding, UncompressedEncoding}; +use group::{prime::PrimeCurveAffine, GroupEncoding, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index cbe883aa7a..1f2d964a1b 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use futures::Future; use ff::{Field, PrimeField}; -use group::{cofactor::CofactorCurveAffine, Curve}; +use group::{prime::PrimeCurveAffine, Curve}; use pairing::Engine; use super::{ParameterSource, Proof}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 4a3d6da6b2..fd7d2b9150 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,7 +1,6 @@ use ff::{Field, PrimeField}; use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, - prime::PrimeGroup, + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; @@ -396,18 +395,6 @@ impl Group for Fr { impl PrimeGroup for Fr {} -impl CofactorGroup for Fr { - type Subgroup = Fr; - - fn clear_cofactor(&self) -> Self::Subgroup { - *self - } - - fn into_subgroup(self) -> CtOption { - CtOption::new(self, Choice::from(1)) - } -} - impl Curve for Fr { type AffineRepr = Fr; @@ -422,7 +409,7 @@ impl WnafGroup for Fr { } } -impl CofactorCurve for Fr { +impl PrimeCurve for Fr { type Affine = Fr; } @@ -441,7 +428,7 @@ impl AsRef<[u8]> for FakePoint { } } -impl CofactorCurveAffine for Fr { +impl PrimeCurveAffine for Fr { type Curve = Fr; type Scalar = Fr; diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 18a376db6f..43c69cb669 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,4 @@ -use group::{cofactor::CofactorCurveAffine, Curve}; +use group::{prime::PrimeCurveAffine, Curve}; use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index 8fdbc70039..efbe02e7e3 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -2,7 +2,7 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Endianness, Field, PrimeField}; use futures::Future; -use group::cofactor::{CofactorCurve, CofactorCurveAffine}; +use group::prime::{PrimeCurve, PrimeCurveAffine}; use std::io; use std::iter; use std::ops::AddAssign; @@ -11,33 +11,33 @@ use std::sync::Arc; use super::SynthesisError; /// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { +pub trait SourceBuilder: Send + Sync + 'static + Clone { type Source: Source; fn new(self) -> Self::Source; } /// A source of bases, like an iterator. -pub trait Source { +pub trait Source { fn next(&mut self) -> Result<&G, SynthesisError>; /// Skips `amt` elements from the source, avoiding deserialization. fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } -pub trait AddAssignFromSource: CofactorCurve { +pub trait AddAssignFromSource: PrimeCurve { /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_from_source::Affine>>( + fn add_assign_from_source::Affine>>( &mut self, source: &mut S, ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); + AddAssign::<&::Affine>::add_assign(self, source.next()?); Ok(()) } } -impl AddAssignFromSource for G where G: CofactorCurve {} +impl AddAssignFromSource for G where G: PrimeCurve {} -impl SourceBuilder for (Arc>, usize) { +impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); fn new(self) -> (Arc>, usize) { @@ -45,7 +45,7 @@ impl SourceBuilder for (Arc>, usize) { } } -impl Source for (Arc>, usize) { +impl Source for (Arc>, usize) { fn next(&mut self) -> Result<&G, SynthesisError> { if self.0.len() <= self.1 { return Err(io::Error::new( @@ -162,8 +162,8 @@ fn multiexp_inner( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CofactorCurve, - S: SourceBuilder<::Affine>, + G: PrimeCurve, + S: SourceBuilder<::Affine>, { // Perform this region of the multiexp let this = { @@ -274,8 +274,8 @@ pub fn multiexp( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CofactorCurve, - S: SourceBuilder<::Affine>, + G: PrimeCurve, + S: SourceBuilder<::Affine>, { let c = if exponents.len() < 32 { 3u32 @@ -296,8 +296,8 @@ where #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { - fn naive_multiexp( - bases: Arc::Affine>>, + fn naive_multiexp( + bases: Arc::Affine>>, exponents: Arc>, ) -> G { assert_eq!(bases.len(), exponents.len()); diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index be383b1676..0be5e56af3 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -4,12 +4,12 @@ use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; use crate::{ - cofactor::{CofactorCurve, CofactorCurveAffine}, + prime::{PrimeCurve, PrimeCurveAffine}, wnaf::WnafGroup, GroupEncoding, UncompressedEncoding, }; -pub fn curve_tests() { +pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -187,7 +187,7 @@ pub fn random_wnaf_tests() { } } -fn random_negation_tests() { +fn random_negation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -217,7 +217,7 @@ fn random_negation_tests() { } } -fn random_doubling_tests() { +fn random_doubling_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -245,7 +245,7 @@ fn random_doubling_tests() { } } -fn random_multiplication_tests() { +fn random_multiplication_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -280,7 +280,7 @@ fn random_multiplication_tests() { } } -fn random_addition_tests() { +fn random_addition_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -360,7 +360,7 @@ fn random_addition_tests() { } } -fn random_transformation_tests() { +fn random_transformation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -397,7 +397,7 @@ fn random_transformation_tests() { } } -fn random_compressed_encoding_tests() { +fn random_compressed_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -423,9 +423,9 @@ fn random_compressed_encoding_tests() { } } -pub fn random_uncompressed_encoding_tests() +pub fn random_uncompressed_encoding_tests() where - ::Affine: UncompressedEncoding, + ::Affine: UncompressedEncoding, { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 453b7497aa..5099307e4f 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -2,7 +2,6 @@ macro_rules! curve_impl { ( $name:expr, $projective:ident, - $subgroup:ident, $affine:ident, $basefield:ident, $scalarfield:ident, @@ -101,21 +100,6 @@ macro_rules! curve_impl { } } - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct $subgroup($projective); - - impl ::std::fmt::Display for $subgroup { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}", self.0) - } - } - - impl From<$subgroup> for $projective { - fn from(val: $subgroup) -> $projective { - val.0 - } - } - impl $affine { fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { let mut res = $projective::identity(); @@ -213,7 +197,7 @@ macro_rules! curve_impl { } } - impl CofactorCurveAffine for $affine { + impl PrimeCurveAffine for $affine { type Scalar = $scalarfield; type Curve = $projective; @@ -270,38 +254,6 @@ macro_rules! curve_impl { } } - impl GroupEncoding for $subgroup { - type Repr = $compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - if let Ok(affine) = bytes.into_affine_unchecked() { - // NB: Decompression guarantees that it is on the curve already. - CtOption::new( - $subgroup(affine.into()), - Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { - 1 - } else { - 0 - }), - ) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - if let Ok(p) = bytes.into_affine_unchecked() { - CtOption::new($subgroup(p.into()), Choice::from(1)) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn to_bytes(&self) -> Self::Repr { - self.0.to_bytes() - } - } - impl GroupEncoding for $affine { type Repr = $compressed; @@ -731,181 +683,6 @@ macro_rules! curve_impl { } } - impl ::std::iter::Sum for $subgroup { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), ::std::ops::Add::add) - } - } - - impl<'r> ::std::iter::Sum<&'r $subgroup> for $subgroup { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), ::std::ops::Add::add) - } - } - - impl ::std::ops::Neg for $subgroup { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - $subgroup(self.0.neg()) - } - } - - impl<'r> ::std::ops::Add<&'r $subgroup> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: &$subgroup) -> Self { - self + &other.0 - } - } - - impl ::std::ops::Add<$subgroup> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: $subgroup) -> Self { - self + &other.0 - } - } - - impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $projective { - fn add_assign(&mut self, other: &$subgroup) { - self.add_assign(&other.0) - } - } - - impl ::std::ops::AddAssign<$subgroup> for $projective { - #[inline] - fn add_assign(&mut self, other: $subgroup) { - self.add_assign(&other.0); - } - } - - impl<'r> ::std::ops::Sub<&'r $subgroup> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: &$subgroup) -> Self { - self - &other.0 - } - } - - impl ::std::ops::Sub<$subgroup> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: $subgroup) -> Self { - self - &other.0 - } - } - - impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $projective { - fn sub_assign(&mut self, other: &$subgroup) { - self.sub_assign(&other.0); - } - } - - impl ::std::ops::SubAssign<$subgroup> for $projective { - #[inline] - fn sub_assign(&mut self, other: $subgroup) { - self.sub_assign(&other.0); - } - } - - impl<'r> ::std::ops::Add<&'r $subgroup> for $subgroup { - type Output = Self; - - #[inline] - fn add(self, other: &$subgroup) -> Self { - $subgroup(self.0 + &other.0) - } - } - - impl ::std::ops::Add<$subgroup> for $subgroup { - type Output = Self; - - #[inline] - fn add(self, other: $subgroup) -> Self { - $subgroup(self.0 + &other.0) - } - } - - impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $subgroup { - fn add_assign(&mut self, other: &$subgroup) { - self.0.add_assign(&other.0) - } - } - - impl ::std::ops::AddAssign<$subgroup> for $subgroup { - #[inline] - fn add_assign(&mut self, other: $subgroup) { - self.0.add_assign(&other.0); - } - } - - impl<'r> ::std::ops::Sub<&'r $subgroup> for $subgroup { - type Output = Self; - - #[inline] - fn sub(self, other: &$subgroup) -> Self { - $subgroup(self.0 - &other.0) - } - } - - impl ::std::ops::Sub<$subgroup> for $subgroup { - type Output = Self; - - #[inline] - fn sub(self, other: $subgroup) -> Self { - $subgroup(self.0 - &other.0) - } - } - - impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $subgroup { - fn sub_assign(&mut self, other: &$subgroup) { - self.0.sub_assign(&other.0); - } - } - - impl ::std::ops::SubAssign<$subgroup> for $subgroup { - #[inline] - fn sub_assign(&mut self, other: $subgroup) { - self.0.sub_assign(&other.0); - } - } - - impl ::std::ops::Mul<<$projective as Group>::Scalar> for $subgroup { - type Output = Self; - - fn mul(mut self, other: <$projective as Group>::Scalar) -> Self { - self.0.mul_assign(&other); - self - } - } - - impl<'r> ::std::ops::Mul<&'r <$projective as Group>::Scalar> for $subgroup { - type Output = Self; - - fn mul(mut self, other: &'r <$projective as Group>::Scalar) -> Self { - self.0.mul_assign(other); - self - } - } - - impl ::std::ops::MulAssign<<$projective as Group>::Scalar> for $subgroup { - fn mul_assign(&mut self, other: <$projective as Group>::Scalar) { - self.0.mul_assign(&other); - } - } - - impl<'r> ::std::ops::MulAssign<&'r <$projective as Group>::Scalar> for $subgroup { - fn mul_assign(&mut self, other: &'r <$projective as Group>::Scalar) { - self.0.mul_assign(other) - } - } - impl Group for $projective { type Scalar = $scalarfield; @@ -994,55 +771,7 @@ macro_rules! curve_impl { } } - impl Group for $subgroup { - type Scalar = $scalarfield; - - fn random(rng: &mut R) -> Self { - $subgroup($projective::random(rng)) - } - - fn identity() -> Self { - $subgroup($projective::identity()) - } - - fn generator() -> Self { - $subgroup($projective::generator()) - } - - fn is_identity(&self) -> Choice { - self.0.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - $subgroup(self.0.double()) - } - } - - impl PrimeGroup for $subgroup {} - - impl CofactorGroup for $projective { - type Subgroup = $subgroup; - - fn clear_cofactor(&self) -> Self::Subgroup { - // This implementation uses the cofactor directly, and differs from the - // bls12_381 crate which uses a multiple of the cofactor. - $subgroup($affine::from(*self).scale_by_cofactor().into()) - } - - fn into_subgroup(self) -> CtOption { - CtOption::new( - $subgroup(self), - Choice::from( - if $affine::from(self).is_in_correct_subgroup_assuming_on_curve() { - 1 - } else { - 0 - }, - ), - ) - } - } + impl PrimeGroup for $projective {} impl Curve for $projective { type AffineRepr = $affine; @@ -1102,7 +831,7 @@ macro_rules! curve_impl { } } - impl CofactorCurve for $projective { + impl PrimeCurve for $projective { type Affine = $affine; } @@ -1206,8 +935,7 @@ pub mod g1 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, - prime::PrimeGroup, + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; @@ -1218,7 +946,6 @@ pub mod g1 { curve_impl!( "G1", G1, - G1Subgroup, G1Affine, Fq, Fr, @@ -1779,8 +1506,7 @@ pub mod g2 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, - prime::PrimeGroup, + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; @@ -1791,7 +1517,6 @@ pub mod g2 { curve_impl!( "G2", G2, - G2Subgroup, G2Affine, Fq2, Fr, diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 99ba70a06f..253b38a8f7 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -24,7 +24,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, MillerLoopResult, MultiMillerLoop}; use ff::{BitIterator, Field, PrimeField}; -use group::{cofactor::CofactorCurveAffine, Group}; +use group::{prime::PrimeCurveAffine, Group}; use rand_core::RngCore; use std::fmt; use std::iter::Sum; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 137694c43a..5d1912f731 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,8 +1,5 @@ use ff::PrimeField; -use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine}, - GroupEncoding, UncompressedEncoding, -}; +use group::{GroupEncoding, UncompressedEncoding}; use super::*; use crate::*; @@ -58,7 +55,7 @@ fn test_pairing_result_against_relic() { })); } -fn uncompressed_test_vectors(expected: &[u8]) +fn uncompressed_test_vectors(expected: &[u8]) where G::Affine: UncompressedEncoding, { @@ -88,7 +85,7 @@ where assert_eq!(&v[..], expected); } -fn compressed_test_vectors(expected: &[u8]) { +fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); let encoded_len = ::Repr::default().as_ref().len(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index bb57d21a79..5f0e3ca101 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,7 +23,7 @@ pub mod bls12_381; use core::ops::Mul; use ff::PrimeField; use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine}, + prime::{PrimeCurve, PrimeCurveAffine}, Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; @@ -35,7 +35,7 @@ pub trait Engine: Sized + 'static + Clone { type Fr: PrimeField; /// The projective representation of an element in G1. - type G1: CofactorCurve + type G1: PrimeCurve + From + GroupOps + GroupOpsOwned @@ -53,7 +53,7 @@ pub trait Engine: Sized + 'static + Clone { + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; /// The projective representation of an element in G2. - type G2: CofactorCurve + type G2: PrimeCurve + From + GroupOps + GroupOpsOwned @@ -80,7 +80,7 @@ pub trait Engine: Sized + 'static + Clone { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. -pub trait PairingCurveAffine: CofactorCurveAffine + UncompressedEncoding { +pub trait PairingCurveAffine: PrimeCurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; type PairingResult: Group; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 2b56ae3dd8..1d03784216 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::Field; -use group::{cofactor::CofactorCurveAffine, Curve, Group}; +use group::{prime::PrimeCurveAffine, Curve, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::Mul; From af9f5f2cf698f879c2eb1a7b8c4196394b7b18c6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 22:03:14 +1200 Subject: [PATCH 146/210] bellman: Migrate to sha2 0.9 --- bellman/Cargo.toml | 2 +- bellman/src/gadgets/sha256.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index b056b6c2bd..5e52192d0f 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -27,7 +27,7 @@ subtle = "2.2.1" hex-literal = "0.2" rand = "0.7" rand_xorshift = "0.2" -sha2 = "0.8" +sha2 = "0.9" [features] groth16 = ["pairing"] diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs index 2db3e6ce13..8f5f66dfe2 100644 --- a/bellman/src/gadgets/sha256.rs +++ b/bellman/src/gadgets/sha256.rs @@ -343,8 +343,8 @@ mod test { for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { let mut h = Sha256::new(); let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - h.input(&data); - let hash_result = h.result(); + h.update(&data); + let hash_result = h.finalize(); let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -366,7 +366,6 @@ mod test { assert!(cs.is_satisfied()); let mut s = hash_result - .as_ref() .iter() .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); From 0db22b1838b620c37335256eed56beba3c177a1f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 22:04:24 +1200 Subject: [PATCH 147/210] bellman: Migrate to bit-vec 0.6 --- bellman/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 5e52192d0f..09b7720ebd 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -10,7 +10,7 @@ version = "0.6.0" edition = "2018" [dependencies] -bit-vec = "0.4.4" +bit-vec = "0.6" blake2s_simd = "0.5" ff = { version = "0.6", path = "../ff" } futures = "0.1" From 5fe2d6a57c47e45ae8127afe65cfdadf62852e94 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 22:14:10 +1200 Subject: [PATCH 148/210] zcash_primitives: ripemd160 0.9 and sha2 0.9 --- zcash_primitives/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 4a99d00909..160c89ae0d 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -26,9 +26,9 @@ log = "0.4" pairing = { version = "0.16", path = "../pairing" } rand = "0.7" rand_core = "0.5.1" -ripemd160 = { version = "0.8", optional = true } +ripemd160 = { version = "0.9", optional = true } secp256k1 = { version = "=0.15.0", optional = true } -sha2 = "0.8" +sha2 = "0.9" subtle = "2.2.1" [dev-dependencies] From bc8a839c612aca720fc840a9ea428065aa1843a6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 22:23:42 +1200 Subject: [PATCH 149/210] Migrate to hex 0.4 --- zcash_client_backend/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index e9d33967e1..5cd64e3b1f 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } -hex = "0.3" +hex = "0.4" pairing = { version = "0.16", path = "../pairing" } protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 160c89ae0d..f513428902 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -20,7 +20,7 @@ crypto_api_chachapoly = "0.2.1" equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" -hex = "0.3" +hex = "0.4" lazy_static = "1" log = "0.4" pairing = { version = "0.16", path = "../pairing" } From 9ced89a1b0c2c19afa911763c133d84f9fd503c6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 23:46:39 +1200 Subject: [PATCH 150/210] zcash_primitives: secp256k1 0.17 --- zcash_primitives/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index f513428902..c8221c71d2 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -27,7 +27,7 @@ pairing = { version = "0.16", path = "../pairing" } rand = "0.7" rand_core = "0.5.1" ripemd160 = { version = "0.9", optional = true } -secp256k1 = { version = "=0.15.0", optional = true } +secp256k1 = { version = "0.17", optional = true } sha2 = "0.9" subtle = "2.2.1" From 751ddad17d1dbfe9127a41a89e445b430fa15853 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 23:50:39 +1200 Subject: [PATCH 151/210] zcash_primitives: crypto_api_chachapoly 0.4 --- zcash_primitives/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index c8221c71d2..6931258739 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -16,7 +16,7 @@ aes = "0.3" blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" -crypto_api_chachapoly = "0.2.1" +crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.2" From f8e74af56cf5d8a9347e4176c30dfbf0be124bb9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jun 2020 23:52:38 +1200 Subject: [PATCH 152/210] zcash_history: quickcheck 0.9 --- zcash_history/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_history/Cargo.toml b/zcash_history/Cargo.toml index 8f254e28ed..d25a1347c3 100644 --- a/zcash_history/Cargo.toml +++ b/zcash_history/Cargo.toml @@ -9,7 +9,7 @@ description = "Library for Zcash blockchain history tools" [dev-dependencies] assert_matches = "1.3.0" -quickcheck = "0.8" +quickcheck = "0.9" [dependencies] bigint = "4" From e210a803507794f8c439c4c465181834447698f4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 00:00:51 +1200 Subject: [PATCH 153/210] zcash_proofs: directories 3 --- zcash_proofs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 1f0ded25f7..1b78341275 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" byteorder = "1" -directories = { version = "1", optional = true } +directories = { version = "3", optional = true } ff = { version = "0.6", path = "../ff" } minreq = { version = "2", features = ["https"], optional = true } pairing = { version = "0.16", path = "../pairing" } From 3cc7d2b89a7a339dd048990a17649dcde8a3ac8d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 26 Jun 2020 00:01:09 +1200 Subject: [PATCH 154/210] Remove unused import --- pairing/benches/bls12_381/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index 7b9b221c55..ed49daa93a 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -10,7 +10,7 @@ use rand_xorshift::XorShiftRng; use group::Group; use pairing::bls12_381::*; -use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; fn bench_pairing_g2_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; From 7f9ee9db2101f4dc9a4f35c8d1ab172696fea879 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 15 Aug 2020 00:40:08 +0100 Subject: [PATCH 155/210] ff_derive: addchain 0.2 --- ff/ff_derive/Cargo.toml | 4 ++-- ff/ff_derive/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml index 4adf28b31c..89a45b315e 100644 --- a/ff/ff_derive/Cargo.toml +++ b/ff/ff_derive/Cargo.toml @@ -16,8 +16,8 @@ edition = "2018" proc-macro = true [dependencies] -addchain = "0.1" -num-bigint = "0.2" +addchain = "0.2" +num-bigint = "0.3" num-traits = "0.2" num-integer = "0.1" proc-macro2 = "1" diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 7e3a4abac8..c5c811ab3c 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -419,7 +419,8 @@ fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec { let mut ret = vec![]; while v > BigUint::zero() { - ret.push((&v % &m).to_u64().unwrap()); + let limb: BigUint = &v % &m; + ret.push(limb.to_u64().unwrap()); v >>= 64; } From 45a78617206184633c46fb10adea8e2c5b33e99b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 16 Aug 2020 11:16:23 +0100 Subject: [PATCH 156/210] zcash_primitives: fpe 0.3 --- zcash_primitives/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 6931258739..34d65711a3 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -12,14 +12,14 @@ license = "MIT OR Apache-2.0" edition = "2018" [dependencies] -aes = "0.3" +aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } -fpe = "0.2" +fpe = "0.3" hex = "0.4" lazy_static = "1" log = "0.4" From d11b60030fdcd00d66a1574a66dc78e08d1e7d23 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 20 May 2020 12:35:35 +1200 Subject: [PATCH 157/210] bls12_381: Implement group traits --- bls12_381/Cargo.toml | 8 +- bls12_381/src/fp.rs | 54 ++++++++++- bls12_381/src/fp2.rs | 9 +- bls12_381/src/g1.rs | 225 ++++++++++++++++++++++++++++++++++++++++++- bls12_381/src/g2.rs | 224 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 515 insertions(+), 5 deletions(-) diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 70810fc7e1..21995b99af 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -29,6 +29,12 @@ path = "../ff" version = "0.6" default-features = false +[dependencies.group] +path = "../group" +version = "0.6" +default-features = false +optional = true + [dependencies.rand_core] version = "0.5" default-features = false @@ -39,7 +45,7 @@ default-features = false [features] default = ["groups", "pairings", "alloc"] -groups = [] +groups = ["group"] pairings = ["groups"] alloc = [] nightly = ["subtle/nightly"] diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs index dfe8849953..3369554515 100644 --- a/bls12_381/src/fp.rs +++ b/bls12_381/src/fp.rs @@ -4,7 +4,7 @@ use core::convert::TryFrom; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::util::{adc, mac, sbb}; @@ -97,6 +97,16 @@ const R2: Fp = Fp([ 0x1198_8fe5_92ca_e3aa, ]); +/// R3 = 2^(384*3) mod p +const R3: Fp = Fp([ + 0xed48_ac6b_d94c_a1e0, + 0x315f_831e_03a7_adf8, + 0x9a53_352a_615e_29dd, + 0x34c0_4e5e_921e_1761, + 0x2512_d435_6572_4728, + 0x0aa6_3460_9175_5d4d, +]); + impl<'a> Neg for &'a Fp { type Output = Fp; @@ -214,6 +224,48 @@ impl Fp { res } + pub(crate) fn random(rng: &mut R) -> Fp { + let mut bytes = [0u8; 96]; + rng.fill_bytes(&mut bytes); + + // Parse the random bytes as a big-endian number, to match Fp encoding order. + Fp::from_u768([ + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[64..72]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[72..80]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[80..88]).unwrap()), + u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[88..96]).unwrap()), + ]) + } + + /// Reduces a big-endian 64-bit limb representation of a 768-bit number. + fn from_u768(limbs: [u64; 12]) -> Fp { + // We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits + // with the higher bits multiplied by 2^384. Thus, we perform two reductions + // + // 1. the lower bits are multiplied by R^2, as normal + // 2. the upper bits are multiplied by R^2 * 2^384 = R^3 + // + // and computing their sum in the field. It remains to see that arbitrary 384-bit + // numbers can be placed into Montgomery form safely using the reduction. The + // reduction works so long as the product is less than R=2^384 multiplied by + // the modulus. This holds because for any `c` smaller than the modulus, we have + // that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the + // reduction always works so long as `c` is in the field; in this case it is either the + // constant `R2` or `R3`. + let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]); + let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } + /// Returns whether or not this element is strictly lexicographically /// larger than its negation. pub fn lexicographically_largest(&self) -> Choice { diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs index 1f5370845b..e9d9275a47 100644 --- a/bls12_381/src/fp2.rs +++ b/bls12_381/src/fp2.rs @@ -2,7 +2,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::fp::Fp; @@ -126,6 +126,13 @@ impl Fp2 { self.c0.is_zero() & self.c1.is_zero() } + pub(crate) fn random(rng: &mut R) -> Fp2 { + Fp2 { + c0: Fp::random(rng), + c1: Fp::random(rng), + } + } + /// Raises this element to p. #[inline(always)] pub fn frobenius_map(&self) -> Self { diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs index c0853e1974..7328c3d3cc 100644 --- a/bls12_381/src/g1.rs +++ b/bls12_381/src/g1.rs @@ -1,9 +1,14 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. use core::borrow::Borrow; +use core::fmt; use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - +use group::{ + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, +}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::fp::Fp; @@ -28,6 +33,12 @@ impl Default for G1Affine { } } +impl fmt::Display for G1Affine { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl<'a> From<&'a G1Projective> for G1Affine { fn from(p: &'a G1Projective) -> G1Affine { let zinv = p.z.invert().unwrap_or(Fp::zero()); @@ -410,6 +421,18 @@ pub struct G1Projective { z: Fp, } +impl Default for G1Projective { + fn default() -> G1Projective { + G1Projective::identity() + } +} + +impl fmt::Display for G1Projective { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl<'a> From<&'a G1Affine> for G1Projective { fn from(p: &'a G1Affine) -> G1Projective { G1Projective { @@ -835,6 +858,206 @@ impl G1Projective { } } +pub struct G1Compressed([u8; 48]); + +impl fmt::Debug for G1Compressed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0[..].fmt(f) + } +} + +impl Default for G1Compressed { + fn default() -> Self { + G1Compressed([0; 48]) + } +} + +impl AsRef<[u8]> for G1Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for G1Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +pub struct G1Uncompressed([u8; 96]); + +impl fmt::Debug for G1Uncompressed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0[..].fmt(f) + } +} + +impl Default for G1Uncompressed { + fn default() -> Self { + G1Uncompressed([0; 96]) + } +} + +impl AsRef<[u8]> for G1Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for G1Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl Group for G1Projective { + type Scalar = Scalar; + + fn random(rng: &mut R) -> Self { + loop { + let x = Fp::random(rng); + let flip_sign = rng.next_u32() % 2 != 0; + + // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) + let p = ((x.square() * x) + B).sqrt().map(|y| G1Affine { + x, + y: if flip_sign { -y } else { y }, + infinity: 0.into(), + }); + + if p.is_some().into() { + let p = p.unwrap().to_curve().clear_cofactor(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + Self::generator() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +impl WnafGroup for G1Projective { + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 12] = + [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } +} + +impl PrimeGroup for G1Projective {} + +impl Curve for G1Projective { + type AffineRepr = G1Affine; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + Self::batch_normalize(p, q); + } + + fn to_affine(&self) -> Self::AffineRepr { + self.into() + } +} + +impl PrimeCurve for G1Projective { + type Affine = G1Affine; +} + +impl PrimeCurveAffine for G1Affine { + type Scalar = Scalar; + type Curve = G1Projective; + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + Self::generator() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + fn to_curve(&self) -> Self::Curve { + self.into() + } +} + +impl GroupEncoding for G1Projective { + type Repr = G1Compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + G1Affine::from_bytes(bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + G1Affine::from_bytes_unchecked(bytes).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + G1Affine::from(self).to_bytes() + } +} + +impl GroupEncoding for G1Affine { + type Repr = G1Compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_compressed(&bytes.0) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_compressed_unchecked(&bytes.0) + } + + fn to_bytes(&self) -> Self::Repr { + G1Compressed(self.to_compressed()) + } +} + +impl UncompressedEncoding for G1Affine { + type Uncompressed = G1Uncompressed; + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed(&bytes.0) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked(&bytes.0) + } + + fn to_uncompressed(&self) -> Self::Uncompressed { + G1Uncompressed(self.to_uncompressed()) + } +} + #[test] fn test_is_on_curve() { assert!(bool::from(G1Affine::identity().is_on_curve())); diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs index e8ce5539ad..ce59b89d46 100644 --- a/bls12_381/src/g2.rs +++ b/bls12_381/src/g2.rs @@ -1,9 +1,14 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. use core::borrow::Borrow; +use core::fmt; use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - +use group::{ + prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, +}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::fp::Fp; @@ -29,6 +34,12 @@ impl Default for G2Affine { } } +impl fmt::Display for G2Affine { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl<'a> From<&'a G2Projective> for G2Affine { fn from(p: &'a G2Projective) -> G2Affine { let zinv = p.z.invert().unwrap_or(Fp2::zero()); @@ -482,6 +493,18 @@ pub struct G2Projective { pub(crate) z: Fp2, } +impl Default for G2Projective { + fn default() -> G2Projective { + G2Projective::identity() + } +} + +impl fmt::Display for G2Projective { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl<'a> From<&'a G2Affine> for G2Projective { fn from(p: &'a G2Affine) -> G2Projective { G2Projective { @@ -1025,6 +1048,205 @@ impl G2Projective { } } +pub struct G2Compressed([u8; 96]); + +impl fmt::Debug for G2Compressed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0[..].fmt(f) + } +} + +impl Default for G2Compressed { + fn default() -> Self { + G2Compressed([0; 96]) + } +} + +impl AsRef<[u8]> for G2Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for G2Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +pub struct G2Uncompressed([u8; 192]); + +impl fmt::Debug for G2Uncompressed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0[..].fmt(f) + } +} + +impl Default for G2Uncompressed { + fn default() -> Self { + G2Uncompressed([0; 192]) + } +} + +impl AsRef<[u8]> for G2Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for G2Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl Group for G2Projective { + type Scalar = Scalar; + + fn random(rng: &mut R) -> Self { + loop { + let x = Fp2::random(rng); + let flip_sign = rng.next_u32() % 2 != 0; + + // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) + let p = ((x.square() * x) + B).sqrt().map(|y| G2Affine { + x, + y: if flip_sign { -y } else { y }, + infinity: 0.into(), + }); + + if p.is_some().into() { + let p = p.unwrap().to_curve().clear_cofactor(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + Self::generator() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +impl WnafGroup for G2Projective { + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } +} + +impl PrimeGroup for G2Projective {} + +impl Curve for G2Projective { + type AffineRepr = G2Affine; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + Self::batch_normalize(p, q); + } + + fn to_affine(&self) -> Self::AffineRepr { + self.into() + } +} + +impl PrimeCurve for G2Projective { + type Affine = G2Affine; +} + +impl PrimeCurveAffine for G2Affine { + type Scalar = Scalar; + type Curve = G2Projective; + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + Self::generator() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + fn to_curve(&self) -> Self::Curve { + self.into() + } +} + +impl GroupEncoding for G2Projective { + type Repr = G2Compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + G2Affine::from_bytes(bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + G2Affine::from_bytes_unchecked(bytes).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + G2Affine::from(self).to_bytes() + } +} + +impl GroupEncoding for G2Affine { + type Repr = G2Compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_compressed(&bytes.0) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_compressed_unchecked(&bytes.0) + } + + fn to_bytes(&self) -> Self::Repr { + G2Compressed(self.to_compressed()) + } +} + +impl UncompressedEncoding for G2Affine { + type Uncompressed = G2Uncompressed; + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed(&bytes.0) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked(&bytes.0) + } + + fn to_uncompressed(&self) -> Self::Uncompressed { + G2Uncompressed(self.to_uncompressed()) + } +} + #[test] fn test_is_on_curve() { assert!(bool::from(G2Affine::identity().is_on_curve())); From eae5df0fb9f436c885534fb0be2a1bbc65e0b42e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 14:09:04 +1200 Subject: [PATCH 158/210] jubjub: Implement group traits --- jubjub/Cargo.toml | 5 + jubjub/src/lib.rs | 405 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 408 insertions(+), 2 deletions(-) diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index 19976d7488..5955e6cb73 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -27,6 +27,11 @@ path = "../ff" version = "0.6" default-features = false +[dependencies.group] +path = "../group" +version = "0.6" +default-features = false + [dependencies.rand_core] version = "0.5" default-features = false diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs index da26fd96cf..d35bc10c7b 100644 --- a/jubjub/src/lib.rs +++ b/jubjub/src/lib.rs @@ -32,7 +32,17 @@ #[macro_use] extern crate std; +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use ff::Field; +use group::{ + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, WnafGroup, +}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; #[macro_use] @@ -49,12 +59,18 @@ const FR_MODULUS_BYTES: [u8; 32] = [ /// This represents a Jubjub point in the affine `(u, v)` /// coordinates. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq)] pub struct AffinePoint { u: Fq, v: Fq, } +impl fmt::Display for AffinePoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl Neg for AffinePoint { type Output = AffinePoint; @@ -101,7 +117,7 @@ impl ConditionallySelectable for AffinePoint { /// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. /// * Double it using `double()`. /// * Compare it with another extended point using `PartialEq` or `ct_eq()`. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq)] pub struct ExtendedPoint { u: Fq, v: Fq, @@ -110,6 +126,12 @@ pub struct ExtendedPoint { t2: Fq, } +impl fmt::Display for ExtendedPoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl ConstantTimeEq for ExtendedPoint { fn ct_eq(&self, other: &Self) -> Choice { // (u/z, v/z) = (u'/z', v'/z') is implied by @@ -140,6 +162,18 @@ impl PartialEq for ExtendedPoint { } } +impl Sum for ExtendedPoint +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + impl Neg for ExtendedPoint { type Output = ExtendedPoint; @@ -367,6 +401,11 @@ impl AffinePoint { } } + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + ExtendedPoint::from(*self).is_identity() + } + /// Multiplies this point by the cofactor, producing an /// `ExtendedPoint` pub fn mul_by_cofactor(&self) -> ExtendedPoint { @@ -638,6 +677,38 @@ impl ExtendedPoint { self.to_niels().multiply(by) } + /// Converts a batch of projective elements into affine elements. + /// + /// This function will panic if `p.len() != q.len()`. + /// + /// This costs 5 multiplications per element, and a field inversion. + fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fq::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `u` field of `AffinePoint` to store the product + // of previous z-coordinates seen. + q.u = acc; + acc *= &p.z; + } + + // This is the inverse, as all z-coordinates are nonzero. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().zip(q.iter_mut()).rev() { + // Compute tmp = 1/z + let tmp = q.u * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc *= &p.z; + + // Set the coordinates to the correct value + q.u = p.u * &tmp; // Multiply by 1/z + q.v = p.v * &tmp; // Multiply by 1/z + } + } + /// This is only for debugging purposes and not /// exposed in the public API. Checks that this /// point is on the curve. @@ -900,6 +971,335 @@ pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator Mul<&'b Fr> for &'a AffinePoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.to_niels().multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint); + +/// This represents a point in the prime-order subgroup of Jubjub, in extended +/// coordinates. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct SubgroupPoint(ExtendedPoint); + +impl From for ExtendedPoint { + fn from(val: SubgroupPoint) -> ExtendedPoint { + val.0 + } +} + +impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint { + fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint { + &val.0 + } +} + +impl fmt::Display for SubgroupPoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ConditionallySelectable for SubgroupPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Sum for SubgroupPoint +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl Neg for SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn neg(self) -> SubgroupPoint { + SubgroupPoint(-self.0) + } +} + +impl Neg for &SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn neg(self) -> SubgroupPoint { + SubgroupPoint(-self.0) + } +} + +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint { + self + &other.0 + } +} + +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint { + self - &other.0 + } +} + +impl_binops_additive!(ExtendedPoint, SubgroupPoint); + +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 + &other.0) + } +} + +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + #[inline] + fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 - &other.0) + } +} + +impl_binops_additive!(SubgroupPoint, SubgroupPoint); + +impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + fn mul(self, other: &'b Fr) -> SubgroupPoint { + SubgroupPoint(self.0.multiply(&other.to_bytes())) + } +} + +impl_binops_multiplicative!(SubgroupPoint, Fr); + +impl Group for ExtendedPoint { + type Scalar = Fr; + + fn random(rng: &mut R) -> Self { + loop { + let v = Fq::random(rng); + let flip_sign = rng.next_u32() % 2 != 0; + + // See AffinePoint::from_bytes for details. + let v2 = v.square(); + let p = ((v2 - Fq::one()) + * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .map(|u| AffinePoint { + u: if flip_sign { -u } else { u }, + v, + }); + + if p.is_some().into() { + let p = p.unwrap().to_curve(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + AffinePoint::generator().into() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +impl Group for SubgroupPoint { + type Scalar = Fr; + + fn random(rng: &mut R) -> Self { + loop { + let p = ExtendedPoint::random(rng).clear_cofactor(); + + if bool::from(!p.is_identity()) { + return p; + } + } + } + + fn identity() -> Self { + SubgroupPoint(ExtendedPoint::identity()) + } + + fn generator() -> Self { + ExtendedPoint::generator().clear_cofactor() + } + + fn is_identity(&self) -> Choice { + self.0.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + SubgroupPoint(self.0.double()) + } +} + +impl WnafGroup for ExtendedPoint { + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + // Copied from bls12_381::g1, should be updated. + const RECOMMENDATIONS: [usize; 12] = + [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } +} + +impl PrimeGroup for SubgroupPoint {} + +impl CofactorGroup for ExtendedPoint { + type Subgroup = SubgroupPoint; + + fn clear_cofactor(&self) -> Self::Subgroup { + SubgroupPoint(self.mul_by_cofactor()) + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(SubgroupPoint(self), self.is_torsion_free()) + } +} + +impl Curve for ExtendedPoint { + type AffineRepr = AffinePoint; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + Self::batch_normalize(p, q); + } + + fn to_affine(&self) -> Self::AffineRepr { + self.into() + } +} + +impl CofactorCurve for ExtendedPoint { + type Affine = AffinePoint; +} + +impl CofactorCurveAffine for AffinePoint { + type Scalar = Fr; + type Curve = ExtendedPoint; + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + // The point with the lowest positive v-coordinate and positive u-coordinate. + AffinePoint { + u: Fq::from_raw([ + 0xe4b3_d35d_f1a7_adfe, + 0xcaf5_5d1b_29bf_81af, + 0x8b0f_03dd_d60a_8187, + 0x62ed_cbb8_bf37_87c8, + ]), + v: Fq::from_raw([ + 0x0000_0000_0000_000b, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + ]), + } + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + fn to_curve(&self) -> Self::Curve { + (*self).into() + } +} + +impl GroupEncoding for ExtendedPoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + AffinePoint::from_bytes(*bytes).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + // We can't avoid curve checks when parsing a compressed encoding. + AffinePoint::from_bytes(*bytes).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + AffinePoint::from(self).to_bytes() + } +} + +impl GroupEncoding for SubgroupPoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint) + } + + fn to_bytes(&self) -> Self::Repr { + self.0.to_bytes() + } +} + +impl GroupEncoding for AffinePoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn to_bytes(&self) -> Self::Repr { + self.to_bytes() + } +} + #[test] fn test_is_on_curve_var() { assert!(AffinePoint::identity().is_on_curve_vartime()); @@ -1157,6 +1557,7 @@ fn find_curve_generator() { assert!(bool::from(b.is_small_order())); assert!(bool::from(b.is_identity())); assert_eq!(FULL_GENERATOR, a); + assert_eq!(AffinePoint::generator(), a); assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); return; } From 4b4a4ee8dcbaa08562048851617ce6e176dfdac6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 24 Jun 2020 13:08:23 +1200 Subject: [PATCH 159/210] bls12_381: Implement pairing traits --- bls12_381/Cargo.toml | 7 +- bls12_381/src/fp12.rs | 8 ++ bls12_381/src/fp6.rs | 9 ++ bls12_381/src/lib.rs | 2 +- bls12_381/src/pairings.rs | 238 +++++++++++++++++++++++++++++++++++++- 5 files changed, 259 insertions(+), 5 deletions(-) diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 21995b99af..1799294f29 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -35,6 +35,11 @@ version = "0.6" default-features = false optional = true +[dependencies.pairing] +path = "../pairing" +version = "0.16" +optional = true + [dependencies.rand_core] version = "0.5" default-features = false @@ -46,7 +51,7 @@ default-features = false [features] default = ["groups", "pairings", "alloc"] groups = ["group"] -pairings = ["groups"] +pairings = ["groups", "pairing"] alloc = [] nightly = ["subtle/nightly"] diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs index 735f91e5d8..f6177cc802 100644 --- a/bls12_381/src/fp12.rs +++ b/bls12_381/src/fp12.rs @@ -4,6 +4,7 @@ use crate::fp6::*; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$. @@ -99,6 +100,13 @@ impl Fp12 { } } + pub(crate) fn random(rng: &mut R) -> Self { + Fp12 { + c0: Fp6::random(rng), + c1: Fp6::random(rng), + } + } + pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { let aa = self.c0.mul_by_01(c0, c1); let bb = self.c1.mul_by_1(c4); diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs index 3f310dc17b..9df150f657 100644 --- a/bls12_381/src/fp6.rs +++ b/bls12_381/src/fp6.rs @@ -3,6 +3,7 @@ use crate::fp2::*; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. @@ -95,6 +96,14 @@ impl Fp6 { } } + pub(crate) fn random(rng: &mut R) -> Self { + Fp6 { + c0: Fp2::random(rng), + c1: Fp2::random(rng), + c2: Fp2::random(rng), + } + } + pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { let b_b = self.c1 * c1; diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs index d5b4d512fb..5e8a7db290 100644 --- a/bls12_381/src/lib.rs +++ b/bls12_381/src/lib.rs @@ -74,7 +74,7 @@ const BLS_X_IS_NEGATIVE: bool = true; mod pairings; #[cfg(feature = "pairings")] -pub use pairings::{pairing, Gt, MillerLoopResult}; +pub use pairings::{pairing, Bls12, Gt, MillerLoopResult}; #[cfg(all(feature = "pairings", feature = "alloc"))] pub use pairings::{multi_miller_loop, G2Prepared}; diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs index ef7180a5e3..440d3038fb 100644 --- a/bls12_381/src/pairings.rs +++ b/bls12_381/src/pairings.rs @@ -1,10 +1,16 @@ +use crate::fp::Fp; use crate::fp12::Fp12; use crate::fp2::Fp2; use crate::fp6::Fp6; -use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; +use crate::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; +use core::borrow::Borrow; +use core::fmt; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - +use group::Group; +use pairing::{Engine, MultiMillerLoop, PairingCurveAffine}; +use rand_core::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; #[cfg(feature = "alloc")] @@ -174,9 +180,15 @@ impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopRes /// /// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to /// keep code and abstractions consistent. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub struct Gt(pub(crate) Fp12); +impl fmt::Display for Gt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + impl ConstantTimeEq for Gt { fn ct_eq(&self, other: &Self) -> Choice { self.0.ct_eq(&other.0) @@ -276,6 +288,166 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { impl_binops_additive!(Gt, Gt); impl_binops_multiplicative!(Gt, Scalar); +impl Sum for Gt +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl Group for Gt { + type Scalar = Scalar; + + fn random(rng: &mut R) -> Self { + loop { + let inner = Fp12::random(rng); + + // Not all elements of Fp12 are elements of the prime-order multiplicative + // subgroup. We run the random element through final_exponentiation to obtain + // a valid element, which requires that it is non-zero. + if !bool::from(inner.is_zero()) { + return MillerLoopResult(inner).final_exponentiation(); + } + } + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + // pairing(&G1Affine::generator(), &G2Affine::generator()) + Gt(Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825, + ]), + c1: Fp::from_raw_unchecked([ + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e, + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f, + ]), + c1: Fp::from_raw_unchecked([ + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae, + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa, + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2, + ]), + c1: Fp::from_raw_unchecked([ + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242, + ]), + }, + }, + }) + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::identity()) + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + #[cfg(feature = "alloc")] #[derive(Clone, Debug)] /// This structure contains cached computations pertaining to a $\mathbb{G}_2$ @@ -558,6 +730,66 @@ fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { (t10, t1, t9) } +impl PairingCurveAffine for G1Affine { + type Pair = G2Affine; + type PairingResult = Gt; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + pairing(self, other) + } +} + +impl PairingCurveAffine for G2Affine { + type Pair = G1Affine; + type PairingResult = Gt; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + pairing(other, self) + } +} + +/// A [`pairing::Engine`] for BLS12-381 pairing operations. +#[derive(Clone, Debug)] +pub struct Bls12; + +impl Engine for Bls12 { + type Fr = Scalar; + type G1 = G1Projective; + type G1Affine = G1Affine; + type G2 = G2Projective; + type G2Affine = G2Affine; + type Gt = Gt; + + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + pairing(p, q) + } +} + +impl pairing::MillerLoopResult for MillerLoopResult { + type Gt = Gt; + + fn final_exponentiation(&self) -> Self::Gt { + self.final_exponentiation() + } +} + +impl MultiMillerLoop for Bls12 { + type G2Prepared = G2Prepared; + type Result = MillerLoopResult; + + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { + multi_miller_loop(terms) + } +} + +#[test] +fn test_gt_generator() { + assert_eq!( + Gt::generator(), + pairing(&G1Affine::generator(), &G2Affine::generator()) + ); +} + #[test] fn test_bilinearity() { use crate::Scalar; From 3a72f081df605dab79d1b583b04fe58681d47f1f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 17 Aug 2020 23:04:42 +0100 Subject: [PATCH 160/210] group: Exclude identity element from output of Group::random --- group/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/group/src/lib.rs b/group/src/lib.rs index 1ac5e31e60..cdf26b5357 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -62,7 +62,8 @@ pub trait Group: /// Scalars modulo the order of this group's scalar field. type Scalar: PrimeField; - /// Returns an element chosen uniformly at random from this group. + /// Returns an element chosen uniformly at random from the non-identity elements of + /// this group. /// /// This function is non-deterministic, and samples from the user-provided RNG. fn random(rng: &mut R) -> Self; From 3200ffc256fdf105653106dffd0c543e1fd48b31 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 18 Aug 2020 14:52:10 +0100 Subject: [PATCH 161/210] group: GroupEncoding::Repr: Default is not required to be a valid element --- group/src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/group/src/lib.rs b/group/src/lib.rs index cdf26b5357..cae3b81cf2 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -105,6 +105,25 @@ pub trait Curve: pub trait GroupEncoding: Sized { /// The encoding of group elements. + /// + /// The `Default` implementation is not required to return a valid point encoding. The + /// bound is present to enable encodings to be constructed generically: + /// ``` + /// # use group::GroupEncoding; + /// # use subtle::CtOption; + /// # struct G; + /// # impl GroupEncoding for G { + /// # type Repr = [u8; 0]; + /// # fn from_bytes(bytes: &Self::Repr) -> CtOption { unimplemented!() } + /// # fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { unimplemented!() } + /// # fn to_bytes(&self) -> Self::Repr { unimplemented!() } + /// # } + /// # let buf = &[0u8; 0][..]; + /// let mut encoding = ::Repr::default(); + /// encoding.as_mut().copy_from_slice(buf); + /// ``` + /// + /// It is recommended that the default should be the all-zeroes encoding. type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize a group element from its encoding. From b86558c63a3097c59f6d9c3df986e64b833696ce Mon Sep 17 00:00:00 2001 From: Matthew Fors Date: Tue, 18 Aug 2020 14:38:09 -0400 Subject: [PATCH 162/210] Decrypt sapling output description given OCK (#271) * decrypt sapling outputs directly with ock * make prf_ock public * unit tests for ock sapling output decryption --- zcash_primitives/src/note_encryption.rs | 236 ++++++++++++++++++++---- 1 file changed, 195 insertions(+), 41 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index bdd6ee1191..c5fe1e8c3c 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -171,7 +171,7 @@ fn kdf_sapling( /// Sapling PRF^ock. /// /// Implemented per section 5.4.2 of the Zcash Protocol Specification. -fn prf_ock( +pub fn prf_ock( ovk: &OutgoingViewingKey, cv: &edwards::Point, cmu: &Fr, @@ -483,15 +483,15 @@ pub fn try_sapling_compact_note_decryption( /// Recovery of the full note plaintext by the sender. /// -/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. +/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ock`. /// If successful, the corresponding Sapling note and memo are returned, along with the /// `PaymentAddress` to which the note was sent. /// -/// Implements section 4.17.3 of the Zcash Protocol Specification. -pub fn try_sapling_output_recovery( +/// Implements part of section 4.17.3 of the Zcash Protocol Specification. +/// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. +pub fn try_sapling_output_recovery_with_ock( height: u32, - ovk: &OutgoingViewingKey, - cv: &edwards::Point, + ock: &[u8], cmu: &Fr, epk: &edwards::Point, enc_ciphertext: &[u8], @@ -500,12 +500,10 @@ pub fn try_sapling_output_recovery( assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE); assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE); - let ock = prf_ock(&ovk, &cv, &cmu, &epk); - let mut op = [0; OUT_CIPHERTEXT_SIZE]; assert_eq!( ChachaPolyIetf::aead_cipher() - .open_to(&mut op, &out_ciphertext, &[], ock.as_bytes(), &[0u8; 12]) + .open_to(&mut op, &out_ciphertext, &[], &ock, &[0u8; 12]) .ok()?, OUT_PLAINTEXT_SIZE ); @@ -588,6 +586,32 @@ pub fn try_sapling_output_recovery( Some((note, to, Memo(memo))) } +/// Recovery of the full note plaintext by the sender. +/// +/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. +/// If successful, the corresponding Sapling note and memo are returned, along with the +/// `PaymentAddress` to which the note was sent. +/// +/// Implements section 4.17.3 of the Zcash Protocol Specification. +pub fn try_sapling_output_recovery( + height: u32, + ovk: &OutgoingViewingKey, + cv: &edwards::Point, + cmu: &Fr, + epk: &edwards::Point, + enc_ciphertext: &[u8], + out_ciphertext: &[u8], +) -> Option<(Note, PaymentAddress, Memo)> { + try_sapling_output_recovery_with_ock::

( + height, + prf_ock(&ovk, &cv, &cmu, &epk).as_bytes(), + cmu, + epk, + enc_ciphertext, + out_ciphertext, + ) +} + #[cfg(test)] mod tests { use crate::{ @@ -604,6 +628,7 @@ mod tests { primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, util::generate_random_rseed, }; + use blake2b_simd::Hash as Blake2bHash; use crypto_api_chachapoly::ChachaPolyIetf; use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr, FrRepr}; @@ -614,9 +639,9 @@ mod tests { use super::{ kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption, - try_sapling_note_decryption, try_sapling_output_recovery, Memo, SaplingNoteEncryption, - COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, - OUT_PLAINTEXT_SIZE, + try_sapling_note_decryption, try_sapling_output_recovery, + try_sapling_output_recovery_with_ock, Memo, SaplingNoteEncryption, COMPACT_NOTE_SIZE, + ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; use crate::{keys::OutgoingViewingKey, JUBJUB}; @@ -741,6 +766,7 @@ mod tests { mut rng: &mut R, ) -> ( OutgoingViewingKey, + Blake2bHash, Fs, edwards::Point, Fr, @@ -750,7 +776,7 @@ mod tests { ) { let ivk = Fs::random(&mut rng); - let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + let (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, rng); assert!(try_sapling_note_decryption::( @@ -769,18 +795,29 @@ mod tests { &enc_ciphertext[..COMPACT_NOTE_SIZE] ) .is_some()); - assert!(try_sapling_output_recovery::( + + let ovk_output_recovery = try_sapling_output_recovery::( height, &ovk, &cv, &cmu, &epk, &enc_ciphertext, - &out_ciphertext - ) - .is_some()); + &out_ciphertext, + ); + let ock_output_recovery = try_sapling_output_recovery_with_ock::( + height, + ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext, + ); + assert!(ovk_output_recovery.is_some()); + assert!(ock_output_recovery.is_some()); + assert_eq!(ovk_output_recovery, ock_output_recovery); - (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) + (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) } fn random_enc_ciphertext_with( @@ -789,6 +826,7 @@ mod tests { mut rng: &mut R, ) -> ( OutgoingViewingKey, + Blake2bHash, Fs, edwards::Point, Fr, @@ -818,9 +856,11 @@ mod tests { let epk = ne.epk(); let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); + let ock = prf_ock(&ovk, &cv, &cmu, &epk); ( ovk, + ock, ivk, cv, cmu, @@ -925,7 +965,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_note_decryption::( @@ -949,7 +989,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_note_decryption::( @@ -973,7 +1013,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_note_decryption::( @@ -997,7 +1037,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, ivk, _, cmu, epk, mut enc_ciphertext, _) = + let (_, _, ivk, _, cmu, epk, mut enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; @@ -1026,7 +1066,7 @@ mod tests { let leadbytes = [0x02, 0x03, 0x01]; for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1060,7 +1100,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1094,7 +1134,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1128,7 +1168,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_compact_note_decryption::( @@ -1152,7 +1192,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_compact_note_decryption::( @@ -1176,7 +1216,7 @@ mod tests { ]; for &height in heights.iter() { - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); + let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( try_sapling_compact_note_decryption::( @@ -1203,7 +1243,7 @@ mod tests { let leadbytes = [0x02, 0x03, 0x01]; for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1237,7 +1277,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1271,7 +1311,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1305,7 +1345,7 @@ mod tests { ]; for &height in heights.iter() { - let (mut ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + let (mut ovk, _, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); ovk.0[0] ^= 0xff; @@ -1324,6 +1364,32 @@ mod tests { } } + #[test] + fn recovery_with_invalid_ock() { + let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; + + for &height in heights.iter() { + let (_, _, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &[0u8; 32], + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } + } + #[test] fn recovery_with_invalid_cv() { let mut rng = OsRng; @@ -1333,7 +1399,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = + let (ovk, _, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1360,7 +1426,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, _, epk, enc_ctext, out_ctext) = + let (ovk, ock, _, cv, _, epk, enc_ctext, out_ctext) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1375,6 +1441,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &Fr::random(&mut rng), + &epk, + &enc_ctext, + &out_ctext + ), + None + ); } } @@ -1387,7 +1464,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); assert_eq!( @@ -1402,6 +1479,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1414,7 +1502,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; @@ -1430,6 +1518,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1442,7 +1541,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = random_enc_ciphertext(height, &mut rng); out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; @@ -1458,6 +1557,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1473,7 +1583,7 @@ mod tests { let leadbytes = [0x02, 0x03, 0x01]; for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1497,6 +1607,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1509,7 +1630,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1533,6 +1654,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1545,7 +1677,7 @@ mod tests { ]; for &height in heights.iter() { - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = random_enc_ciphertext(height, &mut rng); reencrypt_enc_ciphertext( @@ -1569,6 +1701,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } @@ -1582,7 +1725,7 @@ mod tests { for &height in heights.iter() { let ivk = Fs::zero(); - let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, &mut rng); assert_eq!( @@ -1597,6 +1740,17 @@ mod tests { ), None ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); } } From 10c571f2cd46bf52c0f31196b95b695c0341650b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 27 Jun 2020 23:10:15 +1200 Subject: [PATCH 163/210] zcash_primitives: Define generator constants using new crates Includes tests to ensure that the new generator constants match the current zcash_primitives::JUBJUB generators. --- jubjub/src/lib.rs | 22 ++ zcash_primitives/Cargo.toml | 3 + zcash_primitives/src/constants.rs | 355 ++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs index d35bc10c7b..fb4e047efa 100644 --- a/jubjub/src/lib.rs +++ b/jubjub/src/lib.rs @@ -492,6 +492,17 @@ impl AffinePoint { self.v } + /// Returns an `ExtendedPoint` for use in arithmetic operations. + pub const fn to_extended(&self) -> ExtendedPoint { + ExtendedPoint { + u: self.u, + v: self.v, + z: Fq::one(), + t1: self.u, + t2: self.v, + } + } + /// Performs a pre-processing step that produces an `AffineNielsPoint` /// for use in multiple additions. pub const fn to_niels(&self) -> AffineNielsPoint { @@ -1010,6 +1021,17 @@ impl ConditionallySelectable for SubgroupPoint { } } +impl SubgroupPoint { + /// Constructs an AffinePoint given `u` and `v` without checking that the point is on + /// the curve or in the prime-order subgroup. + /// + /// This should only be used for hard-coding constants (e.g. fixed generators); in all + /// other cases, use [`SubgroupPoint::from_bytes`] instead. + pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { + SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) + } +} + impl Sum for SubgroupPoint where T: Borrow, diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 34d65711a3..0aedae3609 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -15,12 +15,15 @@ edition = "2018" aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" +bls12_381 = { version = "0.1", path = "../bls12_381" } byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.6", path = "../ff" } fpe = "0.3" +group = { version = "0.6", path = "../group" } hex = "0.4" +jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" log = "0.4" pairing = { version = "0.16", path = "../pairing" } diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index 37d337ddb9..2d1624b9e3 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -1,5 +1,10 @@ //! Various constants used by the Zcash primitives. +use ff::PrimeField; +use group::Group; +use jubjub::SubgroupPoint; +use lazy_static::lazy_static; + /// 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. @@ -32,3 +37,353 @@ pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv"; /// BLAKE2s Personalization for the nullifier position generator (for computing rho) pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_"; + +/// 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. +pub const PROOF_GENERATION_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3af2_dbef_b96e_2571, + 0xadf2_d038_f2fb_b820, + 0x7043_03f1_e890_6081, + 0x1457_a502_31cd_e2df, + ]), + bls12_381::Scalar::from_raw([ + 0x467a_f9f7_e05d_e8e7, + 0x50df_51ea_f5a1_49d2, + 0xdec9_0184_0f49_48cc, + 0x54b6_d107_18df_2a7a, + ]), +); + +/// The note commitment is randomized over this generator. +pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xa514_3b34_a8e3_6462, + 0xf091_9d06_ffb1_ecda, + 0xa140_9aa1_f33b_ec2c, + 0x26eb_9f8a_9ec7_2a8c, + ]), + bls12_381::Scalar::from_raw([ + 0xd4fc_6365_796c_77ac, + 0x96b7_8bea_fa9c_c44c, + 0x949d_7747_6e26_2c95, + 0x114b_7501_ad10_4c57, + ]), +); + +/// 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. +pub const NULLIFIER_POSITION_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x2ce3_3921_888d_30db, + 0xe81c_ee09_a561_229e, + 0xdb56_b6db_8d80_75ed, + 0x2400_c2e2_e336_2644, + ]), + bls12_381::Scalar::from_raw([ + 0xa3f7_fa36_c72b_0065, + 0xe155_b8e8_ffff_2e42, + 0xfc9e_8a15_a096_ba8f, + 0x6136_9d54_40bf_84a5, + ]), +); + +/// The value commitment is used to check balance between inputs and outputs. The value is +/// placed over this generator. +pub const VALUE_COMMITMENT_VALUE_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3618_3b2c_b4d7_ef51, + 0x9472_c89a_c043_042d, + 0xd861_8ed1_d15f_ef4e, + 0x273f_910d_9ecc_1615, + ]), + bls12_381::Scalar::from_raw([ + 0xa77a_81f5_0667_c8d7, + 0xbc33_32d0_fa1c_cd18, + 0xd322_94fd_8977_4ad6, + 0x466a_7e3a_82f6_7ab1, + ]), +); + +/// The value commitment is randomized over this generator, for privacy. +pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3bce_3b77_9366_4337, + 0xd1d8_da41_af03_744e, + 0x7ff6_826a_d580_04b4, + 0x6800_f4fa_0f00_1cfc, + ]), + bls12_381::Scalar::from_raw([ + 0x3cae_fab9_380b_6a8b, + 0xad46_f1b0_473b_803b, + 0xe6fb_2a6e_1e22_ab50, + 0x6d81_d3a9_cb45_dedb, + ]), +); + +/// The spender proves discrete log with respect to this base at spend time. +pub const SPENDING_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x47bf_4692_0a95_a753, + 0xd5b9_a7d3_ef8e_2827, + 0xd418_a7ff_2675_3b6a, + 0x0926_d4f3_2059_c712, + ]), + bls12_381::Scalar::from_raw([ + 0x3056_32ad_aaf2_b530, + 0x6d65_674d_cedb_ddbc, + 0x53bb_37d0_c21c_fd05, + 0x57a1_019e_6de9_b675, + ]), +); + +/// The generators (for each segment) used in all Pedersen commitments. +pub const PEDERSEN_HASH_GENERATORS: &[SubgroupPoint] = &[ + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x194e_4292_6f66_1b51, + 0x2f0c_718f_6f0f_badd, + 0xb5ea_25de_7ec0_e378, + 0x73c0_16a4_2ded_9578, + ]), + bls12_381::Scalar::from_raw([ + 0x77bf_abd4_3224_3cca, + 0xf947_2e8b_c04e_4632, + 0x79c9_166b_837e_dc5e, + 0x289e_87a2_d352_1b57, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xb981_9dc8_2d90_607e, + 0xa361_ee3f_d48f_df77, + 0x52a3_5a8c_1908_dd87, + 0x15a3_6d1f_0f39_0d88, + ]), + bls12_381::Scalar::from_raw([ + 0x7b0d_c53c_4ebf_1891, + 0x1f3a_beeb_98fa_d3e8, + 0xf789_1142_c001_d925, + 0x015d_8c7f_5b43_fe33, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x76d6_f7c2_b67f_c475, + 0xbae8_e5c4_6641_ae5c, + 0xeb69_ae39_f5c8_4210, + 0x6643_21a5_8246_e2f6, + ]), + bls12_381::Scalar::from_raw([ + 0x80ed_502c_9793_d457, + 0x8bb2_2a7f_1784_b498, + 0xe000_a46c_8e8c_e853, + 0x362e_1500_d24e_ee9e, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x4c76_7804_c1c4_a2cc, + 0x7d02_d50e_654b_87f2, + 0xedc5_f4a9_cff2_9fd5, + 0x323a_6548_ce9d_9876, + ]), + bls12_381::Scalar::from_raw([ + 0x8471_4bec_a335_70e9, + 0x5103_afa1_a11f_6a85, + 0x9107_0acb_d8d9_47b7, + 0x2f7e_e40c_4b56_cad8, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x4680_9430_657f_82d1, + 0xefd5_9313_05f2_f0bf, + 0x89b6_4b4e_0336_2796, + 0x3bd2_6660_00b5_4796, + ]), + bls12_381::Scalar::from_raw([ + 0x9996_8299_c365_8aef, + 0xb3b9_d809_5859_d14c, + 0x3978_3238_1406_c9e5, + 0x494b_c521_03ab_9d0a, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xcb3c_0232_58d3_2079, + 0x1d9e_5ca2_1135_ff6f, + 0xda04_9746_d76d_3ee5, + 0x6344_7b2b_a31b_b28a, + ]), + bls12_381::Scalar::from_raw([ + 0x4360_8211_9f8d_629a, + 0xa802_00d2_c66b_13a7, + 0x64cd_b107_0a13_6a28, + 0x64ec_4689_e8bf_b6e5, + ]), + ), +]; + +/// The maximum number of chunks per segment of the Pedersen hash. +pub const PEDERSEN_HASH_CHUNKS_PER_GENERATOR: usize = 63; + +/// The window size for exponentiation of Pedersen hash generators outside the circuit. +pub const PEDERSEN_HASH_EXP_WINDOW_SIZE: u32 = 8; + +lazy_static! { + /// The exp table for [`PEDERSEN_HASH_GENERATORS`]. + pub static ref PEDERSEN_HASH_EXP_TABLE: Vec>> = + generate_pedersen_hash_exp_table(); +} + +/// Creates the exp table for the Pedersen hash generators. +fn generate_pedersen_hash_exp_table() -> Vec>> { + let window = PEDERSEN_HASH_EXP_WINDOW_SIZE; + + PEDERSEN_HASH_GENERATORS + .iter() + .cloned() + .map(|mut g| { + let mut tables = vec![]; + + let mut num_bits = 0; + while num_bits <= jubjub::Fr::NUM_BITS { + let mut table = Vec::with_capacity(1 << window); + let mut base = SubgroupPoint::identity(); + + for _ in 0..(1 << window) { + table.push(base.clone()); + base += g; + } + + tables.push(table); + num_bits += window; + + for _ in 0..window { + g = g.double(); + } + } + + tables + }) + .collect() +} + +#[cfg(test)] +mod tests { + use group::{cofactor::CofactorGroup, GroupEncoding}; + use jubjub::{ExtendedPoint, SubgroupPoint}; + use pairing::bls12_381::Bls12; + + use super::*; + use crate::{ + jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}, + JUBJUB, + }; + + fn check_edwards(expected: &edwards::Point, actual: SubgroupPoint) { + // Check that the generator is indeed in the subgroup. + assert!(bool::from( + ExtendedPoint::from(actual).into_subgroup().is_some() + )); + + // Check that the generator is correctly derived. + let mut expected_bytes = [0; 32]; + expected.write(&mut expected_bytes[..]).unwrap(); + assert_eq!(expected_bytes[..], actual.to_bytes()[..]); + } + + fn check_generator(expected: FixedGenerators, actual: SubgroupPoint) { + check_edwards(JUBJUB.generator(expected), actual) + } + + #[test] + fn proof_generation_key_base_generator() { + check_generator( + FixedGenerators::ProofGenerationKey, + PROOF_GENERATION_KEY_GENERATOR, + ); + } + + #[test] + fn note_commitment_randomness_generator() { + check_generator( + FixedGenerators::NoteCommitmentRandomness, + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn nullifier_position_generator() { + check_generator( + FixedGenerators::NullifierPosition, + NULLIFIER_POSITION_GENERATOR, + ); + } + + #[test] + fn value_commitment_value_generator() { + check_generator( + FixedGenerators::ValueCommitmentValue, + VALUE_COMMITMENT_VALUE_GENERATOR, + ); + } + + #[test] + fn value_commitment_randomness_generator() { + check_generator( + FixedGenerators::ValueCommitmentRandomness, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn spending_key_generator() { + check_generator( + FixedGenerators::SpendingKeyGenerator, + SPENDING_KEY_GENERATOR, + ); + } + + #[test] + fn pedersen_hash_generators() { + let expected = JUBJUB.pedersen_hash_generators(); + let actual = PEDERSEN_HASH_GENERATORS; + + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual.iter()) { + check_edwards(expected, *actual); + } + } + + #[test] + fn pedersen_hash_exp_table() { + let expected = JUBJUB.pedersen_hash_exp_table(); + let actual = &PEDERSEN_HASH_EXP_TABLE; + + // Same number of Pedersen hash generators. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual.iter()) { + // Same number of windows per generator. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same size table per window. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same table points. + check_edwards(expected, *actual); + } + } + } + } + + #[test] + fn pedersen_hash_chunks_per_generator() { + assert_eq!( + JUBJUB.pedersen_hash_chunks_per_generator(), + PEDERSEN_HASH_CHUNKS_PER_GENERATOR + ); + } +} From 315f00d6d4fcaf308626aa2cd520651635aa0fb6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Jul 2020 22:46:52 +1200 Subject: [PATCH 164/210] zcash_proofs: Define generator constants using new crates Includes tests to ensure that the new generator constants match the current zcash_primitives::JUBJUB generators. --- zcash_proofs/Cargo.toml | 4 + zcash_proofs/src/constants.rs | 293 ++++++++++++++++++++++++++++++++++ zcash_proofs/src/lib.rs | 1 + 3 files changed, 298 insertions(+) create mode 100644 zcash_proofs/src/constants.rs diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 1b78341275..8a917e2f85 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -14,9 +14,13 @@ edition = "2018" [dependencies] bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" +bls12_381 = { version = "0.1", path = "../bls12_381" } byteorder = "1" directories = { version = "3", optional = true } ff = { version = "0.6", path = "../ff" } +group = { version = "0.6", path = "../group" } +jubjub = { version = "0.3", path = "../jubjub" } +lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" diff --git a/zcash_proofs/src/constants.rs b/zcash_proofs/src/constants.rs new file mode 100644 index 0000000000..f02d8e5d0c --- /dev/null +++ b/zcash_proofs/src/constants.rs @@ -0,0 +1,293 @@ +//! Various constants used for the Zcash proofs. + +use bls12_381::Scalar; +use ff::Field; +use group::{Curve, Group}; +use jubjub::ExtendedPoint; +use lazy_static::lazy_static; +use zcash_primitives::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_GENERATORS}; + +/// The `d` constant of the twisted Edwards curve. +pub const EDWARDS_D: Scalar = Scalar::from_raw([ + 0x0106_5fd6_d634_3eb1, + 0x292d_7f6d_3757_9d26, + 0xf5fd_9207_e6bd_7fd4, + 0x2a93_18e7_4bfa_2b48, +]); + +/// The `A` constant of the birationally equivalent Montgomery curve. +pub const MONTGOMERY_A: Scalar = Scalar::from_raw([ + 0x0000_0000_0000_a002, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, +]); + +/// The scaling factor used for conversion to and from the Montgomery form. +pub const MONTGOMERY_SCALE: Scalar = Scalar::from_raw([ + 0x8f45_35f7_cf82_b8d9, + 0xce40_6970_3da8_8abd, + 0x31de_341e_77d7_64e5, + 0x2762_de61_e862_645e, +]); + +/// The number of chunks needed to represent a full scalar during fixed-base +/// exponentiation. +const FIXED_BASE_CHUNKS_PER_GENERATOR: usize = 84; + +/// Reference to a circuit version of a generator for fixed-base salar multiplication. +pub type FixedGenerator = &'static [Vec<(Scalar, Scalar)>]; + +/// Circuit version of a generator for fixed-base salar multiplication. +pub type FixedGeneratorOwned = Vec>; + +lazy_static! { + pub static ref PROOF_GENERATION_KEY_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::PROOF_GENERATION_KEY_GENERATOR); + + pub static ref NOTE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR); + + pub static ref NULLIFIER_POSITION_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::NULLIFIER_POSITION_GENERATOR); + + pub static ref VALUE_COMMITMENT_VALUE_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR); + + pub static ref VALUE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR); + + pub static ref SPENDING_KEY_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::SPENDING_KEY_GENERATOR); + + /// The pre-computed window tables `[-4, 3, 2, 1, 1, 2, 3, 4]` of different magnitudes + /// of the Pedersen hash segment generators. + pub static ref PEDERSEN_CIRCUIT_GENERATORS: Vec>> = + generate_pedersen_circuit_generators(); +} + +/// Creates the 3-bit window table `[0, 1, ..., 8]` for different magnitudes of a fixed +/// generator. +fn generate_circuit_generator(mut gen: jubjub::SubgroupPoint) -> FixedGeneratorOwned { + let mut windows = vec![]; + + for _ in 0..FIXED_BASE_CHUNKS_PER_GENERATOR { + let mut coeffs = vec![(Scalar::zero(), Scalar::one())]; + let mut g = gen.clone(); + for _ in 0..7 { + let g_affine = jubjub::ExtendedPoint::from(g).to_affine(); + coeffs.push((g_affine.get_u(), g_affine.get_v())); + g += gen; + } + windows.push(coeffs); + + // gen = gen * 8 + gen = g; + } + + windows +} + +/// Returns the coordinates of this point's Montgomery curve representation, or `None` if +/// it is the point at infinity. +pub(crate) fn to_montgomery_coords(g: ExtendedPoint) -> Option<(Scalar, Scalar)> { + let g = g.to_affine(); + let (x, y) = (g.get_u(), g.get_v()); + + if y == Scalar::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. + None + } 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. + Some((Scalar::zero(), Scalar::zero())) + } else { + // The mapping is defined as above. + // + // (x, y) -> (u, v) where + // u = (1 + y) / (1 - y) + // v = u / x + + let u = (Scalar::one() + y) * (Scalar::one() - y).invert().unwrap(); + let v = u * x.invert().unwrap(); + + // Scale it into the correct curve constants + // scaling factor = sqrt(4 / (a - d)) + Some((u, v * MONTGOMERY_SCALE)) + } + } +} + +/// Creates the 2-bit window table lookups for each 4-bit "chunk" in each segment of the +/// Pedersen hash. +fn generate_pedersen_circuit_generators() -> Vec>> { + // Process each segment + PEDERSEN_HASH_GENERATORS + .iter() + .cloned() + .map(|mut gen| { + let mut windows = vec![]; + + for _ in 0..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( + to_montgomery_coords(g.into()) + .expect("we never encounter the point at infinity"), + ); + g += gen; + } + windows.push(coeffs); + + // Our chunks are separated by 2 bits to prevent overlap. + for _ in 0..4 { + gen = gen.double(); + } + } + + windows + }) + .collect() +} + +#[cfg(test)] +mod tests { + use bls12_381::Scalar; + use ff::PrimeField; + use pairing::bls12_381::Fr; + use zcash_primitives::{ + jubjub::{FixedGenerators, JubjubParams}, + JUBJUB, + }; + + use super::*; + + fn check_scalar(expected: Fr, actual: Scalar) { + assert_eq!(expected.to_repr().0, actual.to_bytes()); + } + + fn check_generator(expected: FixedGenerators, actual: FixedGenerator) { + let expected = JUBJUB.circuit_generators(expected); + + // Same number of windows per generator. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same size table per window. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same coordinates. + check_scalar(expected.0, actual.0); + check_scalar(expected.1, actual.1); + } + } + } + + #[test] + fn edwards_d() { + check_scalar(*JUBJUB.edwards_d(), EDWARDS_D); + } + + #[test] + fn montgomery_a() { + check_scalar(*JUBJUB.montgomery_a(), MONTGOMERY_A); + } + + #[test] + fn montgomery_scale() { + check_scalar(*JUBJUB.scale(), MONTGOMERY_SCALE); + } + + #[test] + fn fixed_base_chunks_per_generator() { + assert_eq!( + JUBJUB.fixed_base_chunks_per_generator(), + FIXED_BASE_CHUNKS_PER_GENERATOR + ); + } + + #[test] + fn proof_generation_key_base_generator() { + check_generator( + FixedGenerators::ProofGenerationKey, + &PROOF_GENERATION_KEY_GENERATOR, + ); + } + + #[test] + fn note_commitment_randomness_generator() { + check_generator( + FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn nullifier_position_generator() { + check_generator( + FixedGenerators::NullifierPosition, + &NULLIFIER_POSITION_GENERATOR, + ); + } + + #[test] + fn value_commitment_value_generator() { + check_generator( + FixedGenerators::ValueCommitmentValue, + &VALUE_COMMITMENT_VALUE_GENERATOR, + ); + } + + #[test] + fn value_commitment_randomness_generator() { + check_generator( + FixedGenerators::ValueCommitmentRandomness, + &VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn spending_key_generator() { + check_generator( + FixedGenerators::SpendingKeyGenerator, + &SPENDING_KEY_GENERATOR, + ); + } + + #[test] + fn pedersen_circuit_generators() { + let expected = JUBJUB.pedersen_circuit_generators(); + let actual = &PEDERSEN_CIRCUIT_GENERATORS; + + // Same number of Pedersen hash generators. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual.iter()) { + // Same number of windows per generator. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same size table per window. + assert_eq!(expected.len(), actual.len()); + for (expected, actual) in expected.iter().zip(actual) { + // Same coordinates. + check_scalar(expected.0, actual.0); + check_scalar(expected.1, actual.1); + } + } + } + } +} diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 7c574c2955..a94c4a5897 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -18,6 +18,7 @@ use directories::BaseDirs; use std::path::PathBuf; pub mod circuit; +mod constants; mod hashreader; pub mod sapling; pub mod sprout; From fdf06032e3d549cd31aae67eecbb8e4a6692d8d9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 Jul 2020 08:26:54 +1200 Subject: [PATCH 165/210] s/{pairing::bls12_381, zcash_primitives::jubjub}/{bls12_381, jubjub} FINALLY. --- bellman/src/lib.rs | 3 +- zcash_client_backend/Cargo.toml | 3 + zcash_client_backend/src/decrypt.rs | 16 +- zcash_client_backend/src/encoding.rs | 58 +-- zcash_client_backend/src/proto/mod.rs | 25 +- zcash_client_backend/src/wallet.rs | 10 +- zcash_client_backend/src/welding_rig.rs | 29 +- zcash_client_sqlite/Cargo.toml | 2 + zcash_client_sqlite/src/address.rs | 7 +- zcash_client_sqlite/src/lib.rs | 28 +- zcash_client_sqlite/src/scan.rs | 2 - zcash_client_sqlite/src/transact.rs | 26 +- zcash_primitives/benches/pedersen_hash.rs | 5 +- zcash_primitives/src/constants.rs | 24 +- zcash_primitives/src/group_hash.rs | 31 +- zcash_primitives/src/jubjub/mod.rs | 166 +++---- zcash_primitives/src/keys.rs | 117 ++--- zcash_primitives/src/merkle_tree.rs | 17 +- zcash_primitives/src/note_encryption.rs | 319 ++++++------ zcash_primitives/src/pedersen_hash.rs | 55 +-- zcash_primitives/src/primitives.rs | 220 ++++----- zcash_primitives/src/prover.rs | 79 ++- zcash_primitives/src/redjubjub.rs | 264 +++++----- zcash_primitives/src/sapling.rs | 55 +-- .../src/test_vectors/pedersen_hash_vectors.rs | 148 +++--- zcash_primitives/src/transaction/builder.rs | 68 ++- .../src/transaction/components.rs | 71 ++- zcash_primitives/src/transaction/sighash.rs | 3 +- zcash_primitives/src/transaction/tests.rs | 15 +- zcash_primitives/src/util.rs | 15 +- zcash_primitives/src/zip32.rs | 61 +-- zcash_proofs/examples/bench.rs | 30 +- zcash_proofs/src/circuit/ecc.rs | 465 ++++++++---------- zcash_proofs/src/circuit/pedersen_hash.rs | 55 +-- zcash_proofs/src/circuit/sapling.rs | 353 ++++++------- zcash_proofs/src/circuit/sprout/mod.rs | 4 +- zcash_proofs/src/constants.rs | 39 +- zcash_proofs/src/lib.rs | 2 +- zcash_proofs/src/prover.rs | 45 +- zcash_proofs/src/sapling/mod.rs | 17 +- zcash_proofs/src/sapling/prover.rs | 128 ++--- zcash_proofs/src/sapling/verifier.rs | 96 ++-- zcash_proofs/src/sprout.rs | 2 +- 43 files changed, 1343 insertions(+), 1835 deletions(-) diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index 89e7f8bd94..9eb44dd1d8 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -22,8 +22,9 @@ //! }, //! groth16, Circuit, ConstraintSystem, SynthesisError, //! }; +//! use bls12_381::Bls12; //! use ff::PrimeField; -//! use pairing::{bls12_381::Bls12, Engine}; +//! use pairing::Engine; //! use rand::rngs::OsRng; //! use sha2::{Digest, Sha256}; //! diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 5cd64e3b1f..8ceeb6e53b 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,9 +13,12 @@ edition = "2018" [dependencies] bech32 = "0.7" +bls12_381 = { version = "0.1", path = "../bls12_381" } bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } +group = { version = "0.6", path = "../group" } hex = "0.4" +jubjub = { version = "0.3", path = "../jubjub" } pairing = { version = "0.16", path = "../pairing" } protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 1baabbf3de..c20626fca8 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -1,11 +1,10 @@ -use pairing::bls12_381::Bls12; +use group::cofactor::CofactorGroup; use zcash_primitives::{ consensus, note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo}, primitives::{Note, PaymentAddress}, transaction::Transaction, zip32::ExtendedFullViewingKey, - JUBJUB, }; /// A decrypted shielded output. @@ -15,11 +14,11 @@ pub struct DecryptedOutput { /// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData pub index: usize, /// The note within the output. - pub note: Note, + pub note: Note, /// The account that decrypted the note. pub account: usize, /// The address the note was sent to. - pub to: PaymentAddress, + pub to: PaymentAddress, /// The memo included with the note. pub memo: Memo, /// True if this output was recovered using an [`OutgoingViewingKey`], meaning that @@ -45,10 +44,11 @@ pub fn decrypt_transaction( .collect(); for (index, output) in tx.shielded_outputs.iter().enumerate() { - let epk = match output.ephemeral_key.as_prime_order(&JUBJUB) { - Some(p) => p, - None => continue, - }; + let epk = output.ephemeral_key.into_subgroup(); + if epk.is_none().into() { + continue; + } + let epk = epk.unwrap(); for (account, (ivk, ovk)) in vks.iter().enumerate() { let ((note, to, memo), outgoing) = match try_sapling_note_decryption::

( diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 91522fd505..9368132e3e 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -7,14 +7,12 @@ use bech32::{self, Error, FromBase32, ToBase32}; use bs58::{self, decode::Error as Bs58Error}; -use pairing::bls12_381::Bls12; use std::convert::TryInto; use std::io::{self, Write}; use zcash_primitives::{ legacy::TransparentAddress, primitives::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; fn bech32_encode(hrp: &str, write: F) -> String @@ -97,18 +95,15 @@ pub fn decode_extended_full_viewing_key( /// # Examples /// /// ``` -/// use pairing::bls12_381::Bls12; +/// use group::Group; +/// use jubjub::SubgroupPoint; /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::encode_payment_address, /// }; -/// use zcash_primitives::{ -/// jubjub::edwards, -/// primitives::{Diversifier, PaymentAddress}, -/// JUBJUB, -/// }; +/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -117,16 +112,16 @@ pub fn decode_extended_full_viewing_key( /// /// let pa = PaymentAddress::from_parts( /// Diversifier([0u8; 11]), -/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// SubgroupPoint::random(rng), /// ) /// .unwrap(); /// /// assert_eq!( /// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa), -/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe", +/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk", /// ); /// ``` -pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { +pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { bech32_encode(hrp, |w| w.write_all(&addr.to_bytes())) } @@ -135,18 +130,15 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// # Examples /// /// ``` -/// use pairing::bls12_381::Bls12; +/// use group::Group; +/// use jubjub::SubgroupPoint; /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::decode_payment_address, /// }; -/// use zcash_primitives::{ -/// jubjub::edwards, -/// primitives::{Diversifier, PaymentAddress}, -/// JUBJUB, -/// }; +/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -155,19 +147,19 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// /// let pa = PaymentAddress::from_parts( /// Diversifier([0u8; 11]), -/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// SubgroupPoint::random(rng), /// ) /// .unwrap(); /// /// assert_eq!( /// decode_payment_address( /// HRP_SAPLING_PAYMENT_ADDRESS, -/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe", +/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk", /// ), /// Ok(Some(pa)), /// ); /// ``` -pub fn decode_payment_address(hrp: &str, s: &str) -> Result>, Error> { +pub fn decode_payment_address(hrp: &str, s: &str) -> Result, Error> { bech32_decode(hrp, s, |data| { if data.len() != 43 { return None; @@ -175,7 +167,7 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result::from_bytes(&bytes, &JUBJUB) + PaymentAddress::from_bytes(&bytes) }) } @@ -283,12 +275,10 @@ pub fn decode_transparent_address( #[cfg(test)] mod tests { - use pairing::bls12_381::Bls12; + use group::Group; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; - use zcash_primitives::JUBJUB; use zcash_primitives::{ - jubjub::edwards, primitives::{Diversifier, PaymentAddress}, zip32::ExtendedSpendingKey, }; @@ -386,16 +376,14 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress::from_parts( - Diversifier([0u8; 11]), - edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - ) - .unwrap(); + let addr = + PaymentAddress::from_parts(Diversifier([0u8; 11]), jubjub::SubgroupPoint::random(rng)) + .unwrap(); let encoded_main = - "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd"; + "zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z"; let encoded_test = - "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe"; + "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk"; assert_eq!( encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr), @@ -431,11 +419,9 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress::from_parts( - Diversifier([1u8; 11]), - edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - ) - .unwrap(); + let addr = + PaymentAddress::from_parts(Diversifier([1u8; 11]), jubjub::SubgroupPoint::random(rng)) + .unwrap(); let encoded_main = encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr); diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index 0872fbb8de..ee3b8bcdc4 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -1,12 +1,9 @@ //! Generated code for handling light client protobuf structs. use ff::PrimeField; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; -use zcash_primitives::{ - block::{BlockHash, BlockHeader}, - jubjub::{edwards, PrimeOrder}, - JUBJUB, -}; +use group::GroupEncoding; +use std::convert::TryInto; +use zcash_primitives::block::{BlockHash, BlockHeader}; pub mod compact_formats; @@ -65,10 +62,10 @@ impl compact_formats::CompactOutput { /// A convenience method that parses [`CompactOutput.cmu`]. /// /// [`CompactOutput.cmu`]: #structfield.cmu - pub fn cmu(&self) -> Result { - let mut repr = FrRepr::default(); + pub fn cmu(&self) -> Result { + let mut repr = [0; 32]; repr.as_mut().copy_from_slice(&self.cmu[..]); - Fr::from_repr(repr).ok_or(()) + bls12_381::Scalar::from_repr(repr).ok_or(()) } /// Returns the ephemeral public key for this output. @@ -76,8 +73,12 @@ impl compact_formats::CompactOutput { /// A convenience method that parses [`CompactOutput.epk`]. /// /// [`CompactOutput.epk`]: #structfield.epk - pub fn epk(&self) -> Result, ()> { - let p = edwards::Point::::read(&self.epk[..], &JUBJUB).map_err(|_| ())?; - p.as_prime_order(&JUBJUB).ok_or(()) + pub fn epk(&self) -> Result { + let p = jubjub::SubgroupPoint::from_bytes(&self.epk[..].try_into().map_err(|_| ())?); + if p.is_some().into() { + Ok(p.unwrap()) + } else { + Err(()) + } } } diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index dc46a86c7f..f5b17ca864 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -1,9 +1,7 @@ //! Structs representing transaction data scanned from the block chain by a wallet or //! light client. -use pairing::bls12_381::{Bls12, Fr}; use zcash_primitives::{ - jubjub::{edwards, PrimeOrder}, merkle_tree::IncrementalWitness, primitives::{Note, PaymentAddress}, sapling::Node, @@ -36,11 +34,11 @@ pub struct WalletShieldedSpend { /// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription pub struct WalletShieldedOutput { pub index: usize, - pub cmu: Fr, - pub epk: edwards::Point, + pub cmu: bls12_381::Scalar, + pub epk: jubjub::SubgroupPoint, pub account: usize, - pub note: Note, - pub to: PaymentAddress, + pub note: Note, + pub to: PaymentAddress, pub is_change: bool, pub witness: IncrementalWitness, } diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index fd75031539..7f43cb567f 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -5,7 +5,6 @@ use std::collections::HashSet; use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_primitives::{ consensus, - jubjub::fs::Fs, merkle_tree::{CommitmentTree, IncrementalWitness}, note_encryption::try_sapling_compact_note_decryption, sapling::Node, @@ -26,7 +25,7 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx}; fn scan_output( height: u32, (index, output): (usize, CompactOutput), - ivks: &[Fs], + ivks: &[jubjub::Fr], spent_from_accounts: &HashSet, tree: &mut CommitmentTree, existing_witnesses: &mut [&mut IncrementalWitness], @@ -188,18 +187,17 @@ pub fn scan_block( #[cfg(test)] mod tests { use ff::{Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; + use group::GroupEncoding; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ consensus::TestNetwork, - jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, + constants::SPENDING_KEY_GENERATOR, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, primitives::Note, transaction::components::Amount, util::generate_random_rseed, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; use super::scan_block; @@ -212,19 +210,15 @@ mod tests { nf }; let fake_cmu = { - let fake_cmu = Fr::random(rng); + let fake_cmu = bls12_381::Scalar::random(rng); fake_cmu.to_repr().as_ref().to_owned() }; let fake_epk = { - let mut buffer = vec![0; 64]; + let mut buffer = [0; 64]; rng.fill_bytes(&mut buffer); - let fake_esk = Fs::to_uniform(&buffer[..]); - let fake_epk = JUBJUB - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(fake_esk, &JUBJUB); - let mut bytes = vec![]; - fake_epk.write(&mut bytes).unwrap(); - bytes + let fake_esk = jubjub::Fr::from_bytes_wide(&buffer); + let fake_epk = SPENDING_KEY_GENERATOR * fake_esk; + fake_epk.to_bytes().to_vec() }; let mut cspend = CompactSpend::new(); cspend.set_nf(fake_nf); @@ -257,7 +251,7 @@ mod tests { let mut rng = OsRng; let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { - g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), value: value.into(), rseed, @@ -269,9 +263,8 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); + let cmu = note.cm().to_repr().as_ref().to_owned(); + let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); // Create a fake CompactBlock containing the note diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index ec98318007..61467a426a 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -15,6 +15,8 @@ edition = "2018" bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } +group = { version = "0.6", path = "../group" } +jubjub = { version = "0.3", path = "../jubjub" } pairing = { version = "0.16", path = "../pairing" } protobuf = "2" rand_core = "0.5.1" diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs index f72512fa82..bcd5a7e32d 100644 --- a/zcash_client_sqlite/src/address.rs +++ b/zcash_client_sqlite/src/address.rs @@ -1,6 +1,5 @@ //! Structs for handling supported address types. -use pairing::bls12_381::Bls12; use zcash_client_backend::encoding::{ decode_payment_address, decode_transparent_address, encode_payment_address, encode_transparent_address, @@ -19,12 +18,12 @@ use zcash_client_backend::constants::testnet::{ /// An address that funds can be sent to. pub enum RecipientAddress { - Shielded(PaymentAddress), + Shielded(PaymentAddress), Transparent(TransparentAddress), } -impl From> for RecipientAddress { - fn from(addr: PaymentAddress) -> Self { +impl From for RecipientAddress { + fn from(addr: PaymentAddress) -> Self { RecipientAddress::Shielded(addr) } } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 4eb6dcf49d..c0ec73d194 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -96,7 +96,7 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error: mod tests { use crate::Network; use ff::PrimeField; - use pairing::bls12_381::Bls12; + use group::GroupEncoding; use protobuf::Message; use rand_core::{OsRng, RngCore}; use rusqlite::{types::ToSql, Connection}; @@ -111,7 +111,6 @@ mod tests { transaction::components::Amount, util::generate_random_rseed, zip32::ExtendedFullViewingKey, - JUBJUB, }; /// Create a fake CompactBlock at the given height, containing a single output paying @@ -128,7 +127,7 @@ mod tests { let mut rng = OsRng; let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { - g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), value: value.into(), rseed, @@ -140,9 +139,8 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); + let cmu = note.cm().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); // Create a fake CompactBlock containing the note @@ -161,7 +159,7 @@ mod tests { rng.fill_bytes(&mut cb.hash); cb.prevHash.extend_from_slice(&prev_hash.0); cb.vtx.push(ctx); - (cb, note.nf(&extfvk.fvk.vk, 0, &JUBJUB)) + (cb, note.nf(&extfvk.fvk.vk, 0)) } /// Create a fake CompactBlock at the given height, spending a single note from the @@ -171,7 +169,7 @@ mod tests { prev_hash: BlockHash, (nf, in_value): (Vec, Amount), extfvk: ExtendedFullViewingKey, - to: PaymentAddress, + to: PaymentAddress, value: Amount, ) -> CompactBlock { let mut rng = OsRng; @@ -189,7 +187,7 @@ mod tests { // Create a fake Note for the payment ctx.outputs.push({ let note = Note { - g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), value: value.into(), rseed, @@ -201,9 +199,8 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); + let cmu = note.cm().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let mut cout = CompactOutput::new(); @@ -218,7 +215,7 @@ mod tests { let change_addr = extfvk.default_address().unwrap().1; let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { - g_d: change_addr.diversifier().g_d::(&JUBJUB).unwrap(), + g_d: change_addr.diversifier().g_d().unwrap(), pk_d: change_addr.pk_d().clone(), value: (in_value - value).into(), rseed, @@ -230,9 +227,8 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); + let cmu = note.cm().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let mut cout = CompactOutput::new(); diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index a32df41e6b..318b33b671 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -12,7 +12,6 @@ use zcash_primitives::{ merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, transaction::Transaction, - JUBJUB, }; use crate::{ @@ -273,7 +272,6 @@ pub fn scan_cached_blocks, Q: AsRef>( let nf = output.note.nf( &extfvks[output.account].fvk.vk, output.witness.position() as u64, - &JUBJUB, ); // Assumptions: diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 9df019bab5..a7488eaa38 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -1,16 +1,13 @@ //! Functions for creating transactions. use ff::PrimeField; -use pairing::bls12_381::Bls12; use rand_core::{OsRng, RngCore}; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::convert::TryInto; use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ - consensus, - consensus::{NetworkUpgrade, Parameters}, - jubjub::fs::{Fs, FsRepr}, + consensus::{self, NetworkUpgrade, Parameters}, keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, note_encryption::Memo, @@ -22,7 +19,6 @@ use zcash_primitives::{ components::{amount::DEFAULT_FEE, Amount}, }, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; use crate::{ @@ -59,7 +55,7 @@ pub enum OvkPolicy { struct SelectedNoteRow { diversifier: Diversifier, - note: Note, + note: Note, merkle_path: MerklePath, } @@ -243,22 +239,18 @@ pub fn create_to_address>( r.copy_from_slice(&d[..]); Rseed::AfterZip212(r) } else { - let tmp = FsRepr( + let r = jubjub::Fr::from_repr( d[..] .try_into() .map_err(|_| Error(ErrorKind::InvalidNote))?, - ); - let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?; + ) + .ok_or(Error(ErrorKind::InvalidNote))?; Rseed::BeforeZip212(r) } }; - let from = extfvk - .fvk - .vk - .to_payment_address(diversifier, &JUBJUB) - .unwrap(); - let note = from.create_note(note_value as u64, rseed, &JUBJUB).unwrap(); + let from = extfvk.fvk.vk.to_payment_address(diversifier).unwrap(); + let note = from.create_note(note_value as u64, rseed).unwrap(); let merkle_path = { let d: Vec<_> = row.get(3)?; @@ -381,6 +373,7 @@ pub fn create_to_address>( #[cfg(test)] mod tests { + use group::cofactor::CofactorGroup; use rusqlite::Connection; use tempfile::NamedTempFile; use zcash_primitives::{ @@ -390,7 +383,6 @@ mod tests { prover::TxProver, transaction::{components::Amount, Transaction}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; use zcash_proofs::prover::LocalTxProver; @@ -827,7 +819,7 @@ mod tests { &extfvk.fvk.ovk, &output.cv, &output.cmu, - &output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(), + &output.ephemeral_key.into_subgroup().unwrap(), &output.enc_ciphertext, &output.out_ciphertext, ) diff --git a/zcash_primitives/benches/pedersen_hash.rs b/zcash_primitives/benches/pedersen_hash.rs index 6510936021..86cd2366ee 100644 --- a/zcash_primitives/benches/pedersen_hash.rs +++ b/zcash_primitives/benches/pedersen_hash.rs @@ -1,11 +1,8 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use pairing::bls12_381::Bls12; use rand_core::{OsRng, RngCore}; -use zcash_primitives::jubjub::JubjubBls12; use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization}; fn bench_pedersen_hash(c: &mut Criterion) { - let params = JubjubBls12::new(); let rng = &mut OsRng; let bits = (0..510) .map(|_| (rng.next_u32() % 2) != 0) @@ -13,7 +10,7 @@ fn bench_pedersen_hash(c: &mut Criterion) { let personalization = Personalization::MerkleTree(31); c.bench_function("Pedersen hash", |b| { - b.iter(|| pedersen_hash::(personalization, bits.clone(), ¶ms)) + b.iter(|| pedersen_hash(personalization, bits.clone())) }); } diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index 2d1624b9e3..0165f70179 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -273,30 +273,16 @@ fn generate_pedersen_hash_exp_table() -> Vec>> { #[cfg(test)] mod tests { - use group::{cofactor::CofactorGroup, GroupEncoding}; - use jubjub::{ExtendedPoint, SubgroupPoint}; - use pairing::bls12_381::Bls12; + use jubjub::SubgroupPoint; use super::*; use crate::{ - jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}, + jubjub::{FixedGenerators, JubjubParams}, JUBJUB, }; - fn check_edwards(expected: &edwards::Point, actual: SubgroupPoint) { - // Check that the generator is indeed in the subgroup. - assert!(bool::from( - ExtendedPoint::from(actual).into_subgroup().is_some() - )); - - // Check that the generator is correctly derived. - let mut expected_bytes = [0; 32]; - expected.write(&mut expected_bytes[..]).unwrap(); - assert_eq!(expected_bytes[..], actual.to_bytes()[..]); - } - fn check_generator(expected: FixedGenerators, actual: SubgroupPoint) { - check_edwards(JUBJUB.generator(expected), actual) + assert_eq!(JUBJUB.generator(expected), &actual) } #[test] @@ -354,7 +340,7 @@ mod tests { assert_eq!(expected.len(), actual.len()); for (expected, actual) in expected.iter().zip(actual.iter()) { - check_edwards(expected, *actual); + assert_eq!(expected, actual); } } @@ -373,7 +359,7 @@ mod tests { assert_eq!(expected.len(), actual.len()); for (expected, actual) in expected.iter().zip(actual) { // Same table points. - check_edwards(expected, *actual); + assert_eq!(expected, actual); } } } diff --git a/zcash_primitives/src/group_hash.rs b/zcash_primitives/src/group_hash.rs index 6aac847e86..da1a54e14d 100644 --- a/zcash_primitives/src/group_hash.rs +++ b/zcash_primitives/src/group_hash.rs @@ -2,9 +2,8 @@ //! //! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub -use crate::jubjub::{edwards, JubjubEngine, PrimeOrder}; - use ff::PrimeField; +use group::{cofactor::CofactorGroup, Group, GroupEncoding}; use crate::constants; use blake2s_simd::Params; @@ -12,15 +11,11 @@ use blake2s_simd::Params; /// Produces a random point in the Jubjub curve. /// The point is guaranteed to be prime order /// and not the identity. -pub fn group_hash( - tag: &[u8], - personalization: &[u8], - params: &E::Params, -) -> Option> { +pub fn group_hash(tag: &[u8], personalization: &[u8]) -> Option { assert_eq!(personalization.len(), 8); // Check to see that scalar field is 255 bits - assert!(E::Fr::NUM_BITS == 255); + assert!(bls12_381::Scalar::NUM_BITS == 255); let h = Params::new() .hash_length(32) @@ -30,16 +25,18 @@ pub fn group_hash( .update(tag) .finalize(); - match edwards::Point::::read(h.as_ref(), params) { - Ok(p) => { - let p = p.mul_by_cofactor(params); + let p = jubjub::ExtendedPoint::from_bytes(h.as_array()); + if p.is_some().into() { + // ::clear_cofactor is implemented using + // ExtendedPoint::mul_by_cofactor in the jubjub crate. + let p = CofactorGroup::clear_cofactor(&p.unwrap()); - if p != edwards::Point::zero() { - Some(p) - } else { - None - } + if p.is_identity().into() { + None + } else { + Some(p) } - Err(_) => None, + } else { + None } } diff --git a/zcash_primitives/src/jubjub/mod.rs b/zcash_primitives/src/jubjub/mod.rs index c94068161e..1ab04e504d 100644 --- a/zcash_primitives/src/jubjub/mod.rs +++ b/zcash_primitives/src/jubjub/mod.rs @@ -23,15 +23,14 @@ //! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub //! [BLS12-381]: pairing::bls12_381 -use ff::{Field, PrimeField}; +use ff::PrimeField; +use group::{Curve, Group}; use pairing::Engine; use crate::group_hash::group_hash; use crate::constants; -use pairing::bls12_381::{Bls12, Fr}; - /// This is an implementation of the twisted Edwards Jubjub curve. pub mod edwards; @@ -95,7 +94,7 @@ pub trait ToUniform { /// and some pre-computed parameters. pub trait JubjubEngine: Engine { /// The scalar field of the Jubjub curve - type Fs: PrimeField + ToUniform; + type Fs: PrimeField; /// The parameters of Jubjub and the Sapling protocol type Params: JubjubParams; } @@ -112,20 +111,17 @@ pub trait JubjubParams: Sized { /// 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]; + fn pedersen_hash_generators(&self) -> &[jubjub::SubgroupPoint]; /// Returns the exp table for Pedersen hashes. - fn pedersen_hash_exp_table(&self) -> &[Vec>>]; + fn pedersen_hash_exp_table(&self) -> &[Vec>]; /// 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>]; /// 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; + fn generator(&self, base: FixedGenerators) -> &jubjub::SubgroupPoint; /// Returns a window table [0, 1, ..., 8] for different magnitudes of some /// fixed generator. fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; @@ -134,42 +130,41 @@ pub trait JubjubParams: Sized { fn pedersen_hash_exp_window_size() -> u32; } -impl JubjubEngine for Bls12 { - type Fs = self::fs::Fs; +impl JubjubEngine for bls12_381::Bls12 { + type Fs = jubjub::Fr; type Params = JubjubBls12; } pub struct JubjubBls12 { - edwards_d: Fr, - montgomery_a: Fr, - montgomery_2a: Fr, - scale: Fr, + edwards_d: bls12_381::Scalar, + montgomery_a: bls12_381::Scalar, + montgomery_2a: bls12_381::Scalar, + scale: bls12_381::Scalar, - pedersen_hash_generators: Vec>, - pedersen_hash_exp: Vec>>>, - pedersen_circuit_generators: Vec>>, + pedersen_hash_generators: Vec, + pedersen_hash_exp: Vec>>, - fixed_base_generators: Vec>, - fixed_base_circuit_generators: Vec>>, + fixed_base_generators: Vec, + fixed_base_circuit_generators: Vec>>, } -impl JubjubParams for JubjubBls12 { - fn edwards_d(&self) -> &Fr { +impl JubjubParams for JubjubBls12 { + fn edwards_d(&self) -> &bls12_381::Scalar { &self.edwards_d } - fn montgomery_a(&self) -> &Fr { + fn montgomery_a(&self) -> &bls12_381::Scalar { &self.montgomery_a } - fn montgomery_2a(&self) -> &Fr { + fn montgomery_2a(&self) -> &bls12_381::Scalar { &self.montgomery_2a } - fn scale(&self) -> &Fr { + fn scale(&self) -> &bls12_381::Scalar { &self.scale } - fn pedersen_hash_generators(&self) -> &[edwards::Point] { + fn pedersen_hash_generators(&self) -> &[jubjub::SubgroupPoint] { &self.pedersen_hash_generators } - fn pedersen_hash_exp_table(&self) -> &[Vec>>] { + fn pedersen_hash_exp_table(&self) -> &[Vec>] { &self.pedersen_hash_exp } fn pedersen_hash_chunks_per_generator(&self) -> usize { @@ -178,13 +173,13 @@ impl JubjubParams for JubjubBls12 { fn fixed_base_chunks_per_generator(&self) -> usize { 84 } - fn pedersen_circuit_generators(&self) -> &[Vec>] { - &self.pedersen_circuit_generators - } - fn generator(&self, base: FixedGenerators) -> &edwards::Point { + fn generator(&self, base: FixedGenerators) -> &jubjub::SubgroupPoint { &self.fixed_base_generators[base as usize] } - fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] { + fn circuit_generators( + &self, + base: FixedGenerators, + ) -> &[Vec<(bls12_381::Scalar, bls12_381::Scalar)>] { &self.fixed_base_circuit_generators[base as usize][..] } fn pedersen_hash_exp_window_size() -> u32 { @@ -194,12 +189,12 @@ impl JubjubParams for JubjubBls12 { impl JubjubBls12 { pub fn new() -> Self { - let montgomery_a = Fr::from_str("40962").unwrap(); + let montgomery_a = bls12_381::Scalar::from_str("40962").unwrap(); let montgomery_2a = montgomery_a.double(); let mut tmp_params = JubjubBls12 { // d = -(10240/10241) - edwards_d: Fr::from_str( + edwards_d: bls12_381::Scalar::from_str( "19257038036680949359750312669786877991949435402254120286184196891950884077233", ) .unwrap(), @@ -208,7 +203,7 @@ impl JubjubBls12 { // 2A = 2.A montgomery_2a, // scaling factor = sqrt(4 / (a - d)) - scale: Fr::from_str( + scale: bls12_381::Scalar::from_str( "17814886934372412843466061268024708274627479829237077604635722030778476050649", ) .unwrap(), @@ -216,7 +211,6 @@ impl JubjubBls12 { // 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![], }; @@ -236,14 +230,10 @@ impl JubjubBls12 { pedersen_hash_generators.push(JubjubBls12::find_group_hash( &segment_number, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - &tmp_params, )); } - JubjubBls12::check_consistency_of_pedersen_hash_generators( - &tmp_params, - &pedersen_hash_generators, - ); + JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); tmp_params.pedersen_hash_generators = pedersen_hash_generators; } @@ -259,21 +249,21 @@ impl JubjubBls12 { let mut tables = vec![]; let mut num_bits = 0; - while num_bits <= fs::Fs::NUM_BITS { + while num_bits <= jubjub::Fr::NUM_BITS { let mut table = Vec::with_capacity(1 << window); - let mut base = edwards::Point::zero(); + let mut base = jubjub::SubgroupPoint::identity(); for _ in 0..(1 << window) { table.push(base.clone()); - base = base.add(&g, &tmp_params); + base += g; } tables.push(table); num_bits += window; for _ in 0..window { - g = g.double(&tmp_params); + g = g.double(); } } @@ -286,53 +276,47 @@ impl JubjubBls12 { // Create the bases for other parts of the protocol { let mut fixed_base_generators = - vec![edwards::Point::zero(); FixedGenerators::Max as usize]; + vec![jubjub::SubgroupPoint::identity(); FixedGenerators::Max as usize]; fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = JubjubBls12::find_group_hash( &[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, - &tmp_params, ); fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = JubjubBls12::find_group_hash( b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - &tmp_params, ); fixed_base_generators[FixedGenerators::NullifierPosition as usize] = JubjubBls12::find_group_hash( &[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, - &tmp_params, ); fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = JubjubBls12::find_group_hash( b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - &tmp_params, ); fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = JubjubBls12::find_group_hash( b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - &tmp_params, ); fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = JubjubBls12::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() { + if p1.is_identity().into() { panic!("Neutral element!"); } @@ -346,38 +330,6 @@ impl JubjubBls12 { 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 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.to_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. { @@ -386,11 +338,12 @@ impl JubjubBls12 { 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 coeffs = vec![(bls12_381::Scalar::zero(), bls12_381::Scalar::one())]; let mut g = gen.clone(); for _ in 0..7 { - coeffs.push(g.to_xy()); - g = g.add(&gen, &tmp_params); + let g_affine = jubjub::ExtendedPoint::from(g).to_affine(); + coeffs.push((g_affine.get_u(), g_affine.get_v())); + g += gen; } windows.push(coeffs); @@ -406,17 +359,13 @@ impl JubjubBls12 { tmp_params } - fn find_group_hash( - m: &[u8], - personalization: &[u8; 8], - params: &E::Params, - ) -> edwards::Point { + fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> jubjub::SubgroupPoint { let mut tag = m.to_vec(); let i = tag.len(); tag.push(0u8); loop { - let gh = group_hash(&tag, personalization, params); + let gh = group_hash(&tag, personalization); // We don't want to overflow and start reusing generators assert!(tag[i] != u8::max_value()); @@ -430,19 +379,18 @@ impl JubjubBls12 { /// Check for simple relations between the generators, that make finding collisions easy; /// far worse than spec inconsistencies! - fn check_consistency_of_pedersen_hash_generators( - tmp_params: &E::Params, - pedersen_hash_generators: &[edwards::Point], + fn check_consistency_of_pedersen_hash_generators( + pedersen_hash_generators: &[jubjub::SubgroupPoint], ) { for (i, p1) in pedersen_hash_generators.iter().enumerate() { - if p1 == &edwards::Point::zero() { + if p1.is_identity().into() { panic!("Neutral element!"); } for p2 in pedersen_hash_generators.iter().skip(i + 1) { if p1 == p2 { panic!("Duplicate generator!"); } - if p1 == &p2.negate() { + if *p1 == -p2 { panic!("Inverse generator!"); } } @@ -456,8 +404,8 @@ impl JubjubBls12 { if k == j || k == i { continue; } - let sum = &p2.add(&p3, &tmp_params); - if sum == p1 { + let sum = p2 + p3; + if sum == *p1 { panic!("Linear relation between generators!"); } } @@ -468,6 +416,7 @@ impl JubjubBls12 { #[test] fn test_jubjub_bls12() { + use bls12_381::Bls12; use hex_literal::hex; let params = JubjubBls12::new(); @@ -477,7 +426,7 @@ fn test_jubjub_bls12() { let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); let q = edwards::Point::::get_for_y( - Fr::from_str( + bls12_381::Scalar::from_str( "22440861827555040311190986994816762244378363690614952020532787748720529117853", ) .unwrap(), @@ -492,7 +441,7 @@ fn test_jubjub_bls12() { let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); let q = edwards::Point::::get_for_y( - Fr::from_str( + bls12_381::Scalar::from_str( "22440861827555040311190986994816762244378363690614952020532787748720529117853", ) .unwrap(), @@ -507,9 +456,7 @@ fn test_jubjub_bls12() { #[test] #[should_panic(expected = "Linear relation between generators!")] fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { - let params = JubjubBls12::new(); - - let mut pedersen_hash_generators: Vec> = vec![]; + let mut pedersen_hash_generators: Vec = vec![]; use byteorder::{LittleEndian, WriteBytesExt}; @@ -522,7 +469,6 @@ fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation( let p = JubjubBls12::find_group_hash( &segment_number, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - ¶ms, ); pedersen_hash_generators.push(p); } @@ -531,7 +477,7 @@ fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation( let p2 = pedersen_hash_generators[1].clone(); //test for linear relation - pedersen_hash_generators.push(p1.add(&p2, ¶ms)); + pedersen_hash_generators.push(p1 + p2); - JubjubBls12::check_consistency_of_pedersen_hash_generators(¶ms, &pedersen_hash_generators); + JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); } diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index 50c2de5cee..2e2d61eeca 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -5,12 +5,14 @@ //! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents use crate::{ - jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown}, + constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, primitives::{ProofGenerationKey, ViewingKey}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use ff::PrimeField; +use group::{Group, GroupEncoding}; use std::io::{self, Read, Write}; +use subtle::CtOption; pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; @@ -37,47 +39,45 @@ pub struct OutgoingViewingKey(pub [u8; 32]); /// A Sapling expanded spending key #[derive(Clone)] -pub struct ExpandedSpendingKey { - pub ask: E::Fs, - pub nsk: E::Fs, +pub struct ExpandedSpendingKey { + pub ask: jubjub::Fr, + pub nsk: jubjub::Fr, pub ovk: OutgoingViewingKey, } /// A Sapling full viewing key #[derive(Debug)] -pub struct FullViewingKey { - pub vk: ViewingKey, +pub struct FullViewingKey { + pub vk: ViewingKey, pub ovk: OutgoingViewingKey, } -impl ExpandedSpendingKey { +impl ExpandedSpendingKey { pub fn from_spending_key(sk: &[u8]) -> Self { - let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes()); - let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes()); + let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array()); + let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array()); let mut ovk = OutgoingViewingKey([0u8; 32]); ovk.0 .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); ExpandedSpendingKey { ask, nsk, ovk } } - pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey { + pub fn proof_generation_key(&self) -> ProofGenerationKey { ProofGenerationKey { - ak: params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(self.ask, params), + ak: SPENDING_KEY_GENERATOR * self.ask, nsk: self.nsk, } } pub fn read(mut reader: R) -> io::Result { - let mut ask_repr = ::Repr::default(); + let mut ask_repr = [0; 32]; reader.read_exact(ask_repr.as_mut())?; - let ask = E::Fs::from_repr(ask_repr) + let ask = jubjub::Fr::from_repr(ask_repr) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?; - let mut nsk_repr = ::Repr::default(); + let mut nsk_repr = [0; 32]; reader.read_exact(nsk_repr.as_mut())?; - let nsk = E::Fs::from_repr(nsk_repr) + let nsk = jubjub::Fr::from_repr(nsk_repr) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; let mut ovk = [0; 32]; @@ -106,7 +106,7 @@ impl ExpandedSpendingKey { } } -impl Clone for FullViewingKey { +impl Clone for FullViewingKey { fn clone(&self) -> Self { FullViewingKey { vk: ViewingKey { @@ -118,49 +118,42 @@ impl Clone for FullViewingKey { } } -impl FullViewingKey { - pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey, params: &E::Params) -> Self { +impl FullViewingKey { + pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self { FullViewingKey { vk: ViewingKey { - ak: params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(expsk.ask, params), - nk: params - .generator(FixedGenerators::ProofGenerationKey) - .mul(expsk.nsk, params), + ak: SPENDING_KEY_GENERATOR * expsk.ask, + nk: PROOF_GENERATION_KEY_GENERATOR * expsk.nsk, }, ovk: expsk.ovk, } } - pub fn read(mut reader: R, params: &E::Params) -> io::Result { - let ak = edwards::Point::::read(&mut reader, params)?; - let ak = match ak.as_prime_order(params) { - Some(p) => p, - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "ak not in prime-order subgroup", - )); - } + pub fn read(mut reader: R) -> io::Result { + let ak = { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity())) + }; + let nk = { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + jubjub::SubgroupPoint::from_bytes(&buf) }; - if ak == edwards::Point::zero() { + if ak.is_none().into() { return Err(io::Error::new( - io::ErrorKind::InvalidData, + io::ErrorKind::InvalidInput, "ak not of prime order", )); } - - let nk = edwards::Point::::read(&mut reader, params)?; - let nk = match nk.as_prime_order(params) { - Some(p) => p, - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "nk not in prime-order subgroup", - )); - } - }; + if nk.is_none().into() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "nk not in prime-order subgroup", + )); + } + let ak = ak.unwrap(); + let nk = nk.unwrap(); let mut ovk = [0; 32]; reader.read_exact(&mut ovk)?; @@ -172,8 +165,8 @@ impl FullViewingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.vk.ak.write(&mut writer)?; - self.vk.nk.write(&mut writer)?; + writer.write_all(&self.vk.ak.to_bytes())?; + writer.write_all(&self.vk.nk.to_bytes())?; writer.write_all(&self.ovk.0)?; Ok(()) @@ -189,35 +182,31 @@ impl FullViewingKey { #[cfg(test)] mod tests { - use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}; - use pairing::bls12_381::Bls12; - use std::error::Error; + use group::{Group, GroupEncoding}; use super::FullViewingKey; - use crate::JUBJUB; + use crate::constants::SPENDING_KEY_GENERATOR; #[test] fn ak_must_be_prime_order() { let mut buf = [0; 96]; - let identity = edwards::Point::::zero(); + let identity = jubjub::SubgroupPoint::identity(); // Set both ak and nk to the identity. - identity.write(&mut buf[0..32]).unwrap(); - identity.write(&mut buf[32..64]).unwrap(); + buf[0..32].copy_from_slice(&identity.to_bytes()); + buf[32..64].copy_from_slice(&identity.to_bytes()); // ak is not allowed to be the identity. assert_eq!( - FullViewingKey::::read(&buf[..], &JUBJUB) - .unwrap_err() - .description(), + FullViewingKey::read(&buf[..]).unwrap_err().to_string(), "ak not of prime order" ); // Set ak to a basepoint. - let basepoint = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator); - basepoint.write(&mut buf[0..32]).unwrap(); + let basepoint = SPENDING_KEY_GENERATOR; + buf[0..32].copy_from_slice(&basepoint.to_bytes()); // nk is allowed to be the identity. - assert!(FullViewingKey::::read(&buf[..], &JUBJUB).is_ok()); + assert!(FullViewingKey::read(&buf[..]).is_ok()); } } diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index 9aa3ed5831..ec9103d196 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -194,13 +194,7 @@ impl CommitmentTree { /// # Examples /// /// ``` -/// extern crate ff; -/// extern crate pairing; -/// extern crate rand_core; -/// extern crate zcash_primitives; -/// /// use ff::{Field, PrimeField}; -/// use pairing::bls12_381::Fr; /// use rand_core::OsRng; /// use zcash_primitives::{ /// merkle_tree::{CommitmentTree, IncrementalWitness}, @@ -211,13 +205,13 @@ impl CommitmentTree { /// /// let mut tree = CommitmentTree::::new(); /// -/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); -/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); /// let mut witness = IncrementalWitness::from_tree(&tree); /// assert_eq!(witness.position(), 1); /// assert_eq!(tree.root(), witness.root()); /// -/// let cmu = Node::new(Fr::random(&mut rng).to_repr()); +/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr()); /// tree.append(cmu); /// witness.append(cmu); /// assert_eq!(tree.root(), witness.root()); @@ -512,7 +506,6 @@ mod tests { use crate::sapling::Node; use hex; - use pairing::bls12_381::FrRepr; use std::convert::TryInto; use std::io::{self, Read, Write}; @@ -1016,9 +1009,9 @@ mod tests { let mut paths_i = 0; let mut witness_ser_i = 0; for i in 0..16 { - let cm = FrRepr(hex::decode(commitments[i]).unwrap()[..].try_into().unwrap()); + let cm = hex::decode(commitments[i]).unwrap(); - let cm = Node::new(cm); + let cm = Node::new(cm[..].try_into().unwrap()); // Witness here witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm)); diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index c5fe1e8c3c..b2906d8ec6 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,26 +1,20 @@ //! Implementation of in-band secret distribution for Zcash transactions. use crate::{ - consensus, - consensus::{NetworkUpgrade, ZIP212_GRACE_PERIOD}, - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - PrimeOrder, Unknown, - }, + consensus::{self, NetworkUpgrade, ZIP212_GRACE_PERIOD}, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use ff::PrimeField; -use pairing::bls12_381::{Bls12, Fr}; +use group::{cofactor::CofactorGroup, GroupEncoding}; use rand_core::{CryptoRng, RngCore}; use std::convert::TryInto; use std::fmt; use std::str; -use crate::{keys::OutgoingViewingKey, JUBJUB}; +use crate::keys::OutgoingViewingKey; pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; @@ -138,34 +132,24 @@ impl str::FromStr for Memo { /// Sapling key agreement for note encryption. /// /// Implements section 5.4.4.3 of the Zcash Protocol Specification. -pub fn sapling_ka_agree<'a, P>(esk: &Fs, pk_d: &'a P) -> edwards::Point -where - edwards::Point: From<&'a P>, -{ - let p: edwards::Point = pk_d.into(); - - // Multiply by 8 - let p = p.mul_by_cofactor(&JUBJUB); - - // Multiply by esk - p.mul(*esk, &JUBJUB) +pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint { + // [8 esk] pk_d + // ::clear_cofactor is implemented using + // ExtendedPoint::mul_by_cofactor in the jubjub crate. + CofactorGroup::clear_cofactor(&(pk_d * esk)) } /// Sapling KDF for note encryption. /// /// Implements section 5.4.4.4 of the Zcash Protocol Specification. -fn kdf_sapling( - dhsecret: edwards::Point, - epk: &edwards::Point, -) -> Blake2bHash { - let mut input = [0u8; 64]; - dhsecret.write(&mut input[0..32]).unwrap(); - epk.write(&mut input[32..64]).unwrap(); - +fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::SubgroupPoint) -> Blake2bHash { Blake2bParams::new() .hash_length(32) .personal(KDF_SAPLING_PERSONALIZATION) - .hash(&input) + .to_state() + .update(&dhsecret.to_bytes()) + .update(&epk.to_bytes()) + .finalize() } /// Sapling PRF^ock. @@ -173,20 +157,19 @@ fn kdf_sapling( /// Implemented per section 5.4.2 of the Zcash Protocol Specification. pub fn prf_ock( ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, ) -> Blake2bHash { - let mut ock_input = [0u8; 128]; - ock_input[0..32].copy_from_slice(&ovk.0); - cv.write(&mut ock_input[32..64]).unwrap(); - ock_input[64..96].copy_from_slice(cmu.to_repr().as_ref()); - epk.write(&mut ock_input[96..128]).unwrap(); - Blake2bParams::new() .hash_length(32) .personal(PRF_OCK_PERSONALIZATION) - .hash(&ock_input) + .to_state() + .update(&ovk.0) + .update(&cv.to_bytes()) + .update(&cmu.to_repr()) + .update(&epk.to_bytes()) + .finalize() } /// An API for encrypting Sapling notes. @@ -207,42 +190,39 @@ pub fn prf_ock( /// extern crate zcash_primitives; /// /// use ff::Field; -/// use pairing::bls12_381::Bls12; /// use rand_core::OsRng; /// use zcash_primitives::{ -/// jubjub::fs::Fs, /// keys::{OutgoingViewingKey, prf_expand}, /// note_encryption::{Memo, SaplingNoteEncryption}, /// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, -/// JUBJUB, /// }; /// /// let mut rng = OsRng; /// /// let diversifier = Diversifier([0; 11]); -/// let pk_d = diversifier.g_d::(&JUBJUB).unwrap(); +/// let pk_d = diversifier.g_d().unwrap(); /// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); /// let ovk = OutgoingViewingKey([0; 32]); /// /// let value = 1000; -/// let rcv = Fs::random(&mut rng); -/// let cv = ValueCommitment:: { +/// let rcv = jubjub::Fr::random(&mut rng); +/// let cv = ValueCommitment { /// value, /// randomness: rcv.clone(), /// }; -/// let rcm = Fs::random(&mut rng); -/// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap(); -/// let cmu = note.cm(&JUBJUB); +/// let rcm = jubjub::Fr::random(&mut rng); +/// let note = to.create_note(value, Rseed::BeforeZip212(rcm)).unwrap(); +/// let cmu = note.cm(); /// /// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); /// let encCiphertext = enc.encrypt_note_plaintext(); -/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu); +/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm().into(), &cmu); /// ``` pub struct SaplingNoteEncryption { - epk: edwards::Point, - esk: Fs, - note: Note, - to: PaymentAddress, + epk: jubjub::SubgroupPoint, + esk: jubjub::Fr, + note: Note, + to: PaymentAddress, memo: Memo, ovk: OutgoingViewingKey, } @@ -251,13 +231,13 @@ impl SaplingNoteEncryption { /// Creates a new encryption context for the given note. pub fn new( ovk: OutgoingViewingKey, - note: Note, - to: PaymentAddress, + note: Note, + to: PaymentAddress, memo: Memo, rng: &mut R, ) -> SaplingNoteEncryption { let esk = note.generate_or_derive_esk(rng); - let epk = note.g_d.mul(esk, &JUBJUB); + let epk = note.g_d * esk; SaplingNoteEncryption { epk, @@ -270,18 +250,18 @@ impl SaplingNoteEncryption { } /// Exposes the ephemeral secret key being used to encrypt this note. - pub fn esk(&self) -> &Fs { + pub fn esk(&self) -> &jubjub::Fr { &self.esk } /// Exposes the ephemeral public key being used to encrypt this note. - pub fn epk(&self) -> &edwards::Point { + pub fn epk(&self) -> &jubjub::SubgroupPoint { &self.epk } /// Generates `encCiphertext` for this note. pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] { - let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d()); + let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d().into()); let key = kdf_sapling(shared_secret, &self.epk); // Note plaintext encoding is defined in section 5.5 of the Zcash Protocol @@ -319,13 +299,13 @@ impl SaplingNoteEncryption { /// Generates `outCiphertext` for this note. pub fn encrypt_outgoing_plaintext( &self, - cv: &edwards::Point, - cmu: &Fr, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, ) -> [u8; OUT_CIPHERTEXT_SIZE] { let key = prf_ock(&self.ovk, &cv, &cmu, &self.epk); let mut input = [0u8; OUT_PLAINTEXT_SIZE]; - self.note.pk_d.write(&mut input[0..32]).unwrap(); + input[0..32].copy_from_slice(&self.note.pk_d.to_bytes()); input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref()); let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; @@ -342,11 +322,11 @@ impl SaplingNoteEncryption { fn parse_note_plaintext_without_memo( height: u32, - ivk: &Fs, - epk: &edwards::Point, - cmu: &Fr, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, plaintext: &[u8], -) -> Option<(Note, PaymentAddress)> { +) -> Option<(Note, PaymentAddress)> { // Check note plaintext version if !plaintext_version_is_valid::

(height, plaintext[0]) { return None; @@ -362,27 +342,25 @@ fn parse_note_plaintext_without_memo( .expect("slice is the correct length"); let rseed = if plaintext[0] == 0x01 { - let rcm = Fs::from_repr(FsRepr(r))?; + let rcm = jubjub::Fr::from_repr(r)?; Rseed::BeforeZip212(rcm) } else { Rseed::AfterZip212(r) }; let diversifier = Diversifier(d); - let pk_d = diversifier - .g_d::(&JUBJUB)? - .mul(ivk.to_repr(), &JUBJUB); + let pk_d = diversifier.g_d()? * ivk; let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rseed, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed).unwrap(); - if note.cm(&JUBJUB) != *cmu { + if note.cm() != *cmu { // Published commitment doesn't match calculated commitment return None; } if let Some(derived_esk) = note.derive_esk() { - if note.g_d.mul(derived_esk, &JUBJUB) != *epk { + if (note.g_d * derived_esk) != *epk { return None; } } @@ -420,14 +398,14 @@ pub fn plaintext_version_is_valid(height: u32, leadbyt /// Implements section 4.17.2 of the Zcash Protocol Specification. pub fn try_sapling_note_decryption( height: u32, - ivk: &Fs, - epk: &edwards::Point, - cmu: &Fr, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, enc_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress, Memo)> { +) -> Option<(Note, PaymentAddress, Memo)> { assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE); - let shared_secret = sapling_ka_agree(ivk, epk); + let shared_secret = sapling_ka_agree(ivk, epk.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; @@ -463,14 +441,14 @@ pub fn try_sapling_note_decryption( /// [`ZIP 307`]: https://zips.z.cash/zip-0307 pub fn try_sapling_compact_note_decryption( height: u32, - ivk: &Fs, - epk: &edwards::Point, - cmu: &Fr, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, enc_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress)> { +) -> Option<(Note, PaymentAddress)> { assert_eq!(enc_ciphertext.len(), COMPACT_NOTE_SIZE); - let shared_secret = sapling_ka_agree(ivk, epk); + let shared_secret = sapling_ka_agree(ivk, epk.into()); let key = kdf_sapling(shared_secret, &epk); // Start from block 1 to skip over Poly1305 keying output @@ -492,11 +470,11 @@ pub fn try_sapling_compact_note_decryption( pub fn try_sapling_output_recovery_with_ock( height: u32, ock: &[u8], - cmu: &Fr, - epk: &edwards::Point, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, enc_ciphertext: &[u8], out_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress, Memo)> { +) -> Option<(Note, PaymentAddress, Memo)> { assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE); assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE); @@ -508,17 +486,23 @@ pub fn try_sapling_output_recovery_with_ock( OUT_PLAINTEXT_SIZE ); - let pk_d = edwards::Point::::read(&op[0..32], &JUBJUB) - .ok()? - .as_prime_order(&JUBJUB)?; + let pk_d = { + let pk_d = jubjub::SubgroupPoint::from_bytes( + op[0..32].try_into().expect("slice is the correct length"), + ); + if pk_d.is_none().into() { + return None; + } + pk_d.unwrap() + }; - let esk = Fs::from_repr(FsRepr( + let esk = jubjub::Fr::from_repr( op[32..OUT_PLAINTEXT_SIZE] .try_into() .expect("slice is the correct length"), - ))?; + )?; - let shared_secret = sapling_ka_agree(&esk, &pk_d); + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; @@ -550,7 +534,7 @@ pub fn try_sapling_output_recovery_with_ock( .expect("slice is the correct length"); let rseed = if plaintext[0] == 0x01 { - let rcm = Fs::from_repr(FsRepr(r))?; + let rcm = jubjub::Fr::from_repr(r)?; Rseed::BeforeZip212(rcm) } else { Rseed::AfterZip212(r) @@ -560,19 +544,15 @@ pub fn try_sapling_output_recovery_with_ock( memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); let diversifier = Diversifier(d); - if diversifier - .g_d::(&JUBJUB)? - .mul(esk.to_repr(), &JUBJUB) - != *epk - { + if diversifier.g_d()? * esk != *epk { // Published epk doesn't match calculated epk return None; } let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rseed, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed).unwrap(); - if note.cm(&JUBJUB) != *cmu { + if note.cm() != *cmu { // Published commitment doesn't match calculated commitment return None; } @@ -596,12 +576,12 @@ pub fn try_sapling_output_recovery_with_ock( pub fn try_sapling_output_recovery( height: u32, ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, enc_ciphertext: &[u8], out_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress, Memo)> { +) -> Option<(Note, PaymentAddress, Memo)> { try_sapling_output_recovery_with_ock::

( height, prf_ock(&ovk, &cv, &cmu, &epk).as_bytes(), @@ -614,24 +594,11 @@ pub fn try_sapling_output_recovery( #[cfg(test)] mod tests { - use crate::{ - consensus::{ - NetworkUpgrade, - NetworkUpgrade::{Canopy, Sapling}, - Parameters, TestNetwork, ZIP212_GRACE_PERIOD, - }, - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - PrimeOrder, Unknown, - }, - primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, - util::generate_random_rseed, - }; use blake2b_simd::Hash as Blake2bHash; use crypto_api_chachapoly::ChachaPolyIetf; use ff::{Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr, FrRepr}; + use group::Group; + use group::{cofactor::CofactorGroup, GroupEncoding}; use rand_core::OsRng; use rand_core::{CryptoRng, RngCore}; use std::convert::TryInto; @@ -643,7 +610,16 @@ mod tests { try_sapling_output_recovery_with_ock, Memo, SaplingNoteEncryption, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; - use crate::{keys::OutgoingViewingKey, JUBJUB}; + use crate::{ + consensus::{ + NetworkUpgrade, + NetworkUpgrade::{Canopy, Sapling}, + Parameters, TestNetwork, ZIP212_GRACE_PERIOD, + }, + keys::OutgoingViewingKey, + primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, + util::generate_random_rseed, + }; #[test] fn memo_from_str() { @@ -767,14 +743,14 @@ mod tests { ) -> ( OutgoingViewingKey, Blake2bHash, - Fs, - edwards::Point, - Fr, - edwards::Point, + jubjub::Fr, + jubjub::ExtendedPoint, + bls12_381::Scalar, + jubjub::SubgroupPoint, [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { - let ivk = Fs::random(&mut rng); + let ivk = jubjub::Fr::random(&mut rng); let (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, rng); @@ -822,34 +798,34 @@ mod tests { fn random_enc_ciphertext_with( height: u32, - ivk: Fs, + ivk: jubjub::Fr, mut rng: &mut R, ) -> ( OutgoingViewingKey, Blake2bHash, - Fs, - edwards::Point, - Fr, - edwards::Point, + jubjub::Fr, + jubjub::ExtendedPoint, + bls12_381::Scalar, + jubjub::SubgroupPoint, [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { let diversifier = Diversifier([0; 11]); - let pk_d = diversifier.g_d::(&JUBJUB).unwrap().mul(ivk, &JUBJUB); + let pk_d = diversifier.g_d().unwrap() * ivk; let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d); // Construct the value commitment for the proof instance let value = 100; - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), }; - let cv = value_commitment.cm(&JUBJUB).into(); + let cv = value_commitment.cm().into(); let rseed = generate_random_rseed::(height, &mut rng); - let note = pa.create_note(value, rseed, &JUBJUB).unwrap(); - let cmu = note.cm(&JUBJUB); + let note = pa.create_note(value, rseed).unwrap(); + let cmu = note.cm(); let ovk = OutgoingViewingKey([0; 32]); let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), &mut rng); @@ -872,9 +848,9 @@ mod tests { fn reencrypt_enc_ciphertext( ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, enc_ciphertext: &mut [u8; ENC_CIPHERTEXT_SIZE], out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE], modify_plaintext: impl Fn(&mut [u8; NOTE_PLAINTEXT_SIZE]), @@ -889,14 +865,11 @@ mod tests { OUT_PLAINTEXT_SIZE ); - let pk_d = edwards::Point::::read(&op[0..32], &JUBJUB) - .unwrap() - .as_prime_order(&JUBJUB) - .unwrap(); + let pk_d = jubjub::SubgroupPoint::from_bytes(&op[0..32].try_into().unwrap()).unwrap(); - let esk = Fs::from_repr(FsRepr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())).unwrap(); + let esk = jubjub::Fr::from_repr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()).unwrap(); - let shared_secret = sapling_ka_agree(&esk, &pk_d); + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = { @@ -932,7 +905,7 @@ mod tests { break; } } - if d.g_d::(&JUBJUB).is_none() { + if d.g_d().is_none() { break; } } @@ -949,7 +922,7 @@ mod tests { break; } } - if d.g_d::(&JUBJUB).is_some() { + if d.g_d().is_some() { break; } } @@ -970,7 +943,7 @@ mod tests { assert_eq!( try_sapling_note_decryption::( height, - &Fs::random(&mut rng), + &jubjub::Fr::random(&mut rng), &epk, &cmu, &enc_ciphertext @@ -995,7 +968,7 @@ mod tests { try_sapling_note_decryption::( height, &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &jubjub::SubgroupPoint::random(&mut rng), &cmu, &enc_ciphertext ), @@ -1020,7 +993,7 @@ mod tests { height, &ivk, &epk, - &Fr::random(&mut rng), + &bls12_381::Scalar::random(&mut rng), &enc_ciphertext ), None @@ -1173,7 +1146,7 @@ mod tests { assert_eq!( try_sapling_compact_note_decryption::( height, - &Fs::random(&mut rng), + &jubjub::Fr::random(&mut rng), &epk, &cmu, &enc_ciphertext[..COMPACT_NOTE_SIZE] @@ -1198,7 +1171,7 @@ mod tests { try_sapling_compact_note_decryption::( height, &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &jubjub::SubgroupPoint::random(&mut rng), &cmu, &enc_ciphertext[..COMPACT_NOTE_SIZE] ), @@ -1223,7 +1196,7 @@ mod tests { height, &ivk, &epk, - &Fr::random(&mut rng), + &bls12_381::Scalar::random(&mut rng), &enc_ciphertext[..COMPACT_NOTE_SIZE] ), None @@ -1406,7 +1379,7 @@ mod tests { try_sapling_output_recovery::( height, &ovk, - &edwards::Point::::rand(&mut rng, &JUBJUB), + &jubjub::ExtendedPoint::random(&mut rng), &cmu, &epk, &enc_ciphertext, @@ -1434,7 +1407,7 @@ mod tests { height, &ovk, &cv, - &Fr::random(&mut rng), + &bls12_381::Scalar::random(&mut rng), &epk, &enc_ctext, &out_ctext @@ -1445,7 +1418,7 @@ mod tests { try_sapling_output_recovery_with_ock::( height, &ock.as_bytes(), - &Fr::random(&mut rng), + &bls12_381::Scalar::random(&mut rng), &epk, &enc_ctext, &out_ctext @@ -1473,7 +1446,7 @@ mod tests { &ovk, &cv, &cmu, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &jubjub::SubgroupPoint::random(&mut rng), &enc_ciphertext, &out_ciphertext ), @@ -1484,7 +1457,7 @@ mod tests { height, &ock.as_bytes(), &cmu, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + &jubjub::SubgroupPoint::random(&mut rng), &enc_ciphertext, &out_ciphertext ), @@ -1724,7 +1697,7 @@ mod tests { ]; for &height in heights.iter() { - let ivk = Fs::zero(); + let ivk = jubjub::Fr::zero(); let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, &mut rng); @@ -1760,19 +1733,19 @@ mod tests { macro_rules! read_fr { ($field:expr) => {{ - Fr::from_repr(FrRepr($field[..].try_into().unwrap())).unwrap() + bls12_381::Scalar::from_repr($field[..].try_into().unwrap()).unwrap() }}; } macro_rules! read_fs { ($field:expr) => {{ - Fs::from_repr(FsRepr($field[..].try_into().unwrap())).unwrap() + jubjub::Fr::from_repr($field[..].try_into().unwrap()).unwrap() }}; } macro_rules! read_point { ($field:expr) => { - edwards::Point::::read(&$field[..], &JUBJUB).unwrap() + jubjub::ExtendedPoint::from_bytes(&$field).unwrap() }; } @@ -1785,27 +1758,19 @@ mod tests { // let ivk = read_fs!(tv.ivk); - let pk_d = read_point!(tv.default_pk_d) - .as_prime_order(&JUBJUB) - .unwrap(); + let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap(); let rcm = read_fs!(tv.rcm); let cv = read_point!(tv.cv); let cmu = read_fr!(tv.cmu); let esk = read_fs!(tv.esk); - let epk = read_point!(tv.epk).as_prime_order(&JUBJUB).unwrap(); + let epk = read_point!(tv.epk).into_subgroup().unwrap(); // // Test the individual components // - let shared_secret = sapling_ka_agree(&esk, &pk_d); - { - let mut encoded = [0; 32]; - shared_secret - .write(&mut encoded[..]) - .expect("length is not 32 bytes"); - assert_eq!(encoded, tv.shared_secret); - } + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); + assert_eq!(shared_secret.to_bytes(), tv.shared_secret); let k_enc = kdf_sapling(shared_secret, &epk); assert_eq!(k_enc.as_bytes(), tv.k_enc); @@ -1815,10 +1780,8 @@ mod tests { assert_eq!(ock.as_bytes(), tv.ock); let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); - let note = to - .create_note(tv.v, Rseed::BeforeZip212(rcm), &JUBJUB) - .unwrap(); - assert_eq!(note.cm(&JUBJUB), cmu); + let note = to.create_note(tv.v, Rseed::BeforeZip212(rcm)).unwrap(); + assert_eq!(note.cm(), cmu); // // Test decryption diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index 747ffcb8e3..edbc5344bb 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -1,10 +1,14 @@ //! Implementation of the Pedersen hash function used in Sapling. -use crate::jubjub::*; use byteorder::{ByteOrder, LittleEndian}; -use ff::{Endianness, Field, PrimeField}; +use ff::PrimeField; +use group::Group; use std::ops::{AddAssign, Neg}; +use crate::constants::{ + PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_EXP_TABLE, PEDERSEN_HASH_EXP_WINDOW_SIZE, +}; + #[derive(Copy, Clone)] pub enum Personalization { NoteCommitment, @@ -24,27 +28,22 @@ impl Personalization { } } -pub fn pedersen_hash( - personalization: Personalization, - bits: I, - params: &E::Params, -) -> edwards::Point +pub fn pedersen_hash(personalization: Personalization, bits: I) -> jubjub::SubgroupPoint where I: IntoIterator, - 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(); + let mut result = jubjub::SubgroupPoint::identity(); + let mut generators = 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 acc = jubjub::Fr::zero(); + let mut cur = jubjub::Fr::one(); + let mut chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR; let mut encountered_bits = false; // Grab three bits from the input @@ -84,21 +83,20 @@ where break; } - let mut table: &[Vec>] = + let mut table: &[Vec] = &generators.next().expect("we don't have enough generators"); - let window = JubjubBls12::pedersen_hash_exp_window_size() as usize; + let window = PEDERSEN_HASH_EXP_WINDOW_SIZE as usize; let window_mask = (1u64 << window) - 1; - let mut acc = acc.to_repr(); - ::ReprEndianness::toggle_little_endian(&mut acc); + let acc = acc.to_repr(); let num_limbs: usize = acc.as_ref().len() / 8; let mut limbs = vec![0u64; num_limbs + 1]; LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]); - let mut tmp = edwards::Point::zero(); + let mut tmp = jubjub::SubgroupPoint::identity(); let mut pos = 0; - while pos < E::Fs::NUM_BITS as usize { + while pos < jubjub::Fr::NUM_BITS as usize { let u64_idx = pos / 64; let bit_idx = pos % 64; let i = (if bit_idx + window < 64 { @@ -109,13 +107,13 @@ where (limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx)) } & window_mask) as usize; - tmp = tmp.add(&table[0][i], params); + tmp += table[0][i]; pos += window; table = &table[1..]; } - result = result.add(&tmp, params); + result += tmp; } result @@ -123,10 +121,10 @@ where #[cfg(test)] pub mod test { + use group::Curve; use super::*; use crate::test_vectors::pedersen_hash_vectors; - use pairing::bls12_381::Bls12; pub struct TestVector<'a> { pub personalization: Personalization, @@ -142,22 +140,19 @@ pub mod test { assert!(test_vectors.len() > 0); for v in test_vectors.iter() { - let params = &JubjubBls12::new(); - let input_bools: Vec = v.input_bits.iter().map(|&i| i == 1).collect(); // The 6 bits prefix is handled separately assert_eq!(v.personalization.get_bits(), &input_bools[..6]); - let (x, y) = pedersen_hash::( + let p = jubjub::ExtendedPoint::from(pedersen_hash( v.personalization, input_bools.into_iter().skip(6), - params, - ) - .to_xy(); + )) + .to_affine(); - assert_eq!(x.to_string(), v.hash_x); - assert_eq!(y.to_string(), v.hash_y); + assert_eq!(p.get_u().to_string(), v.hash_x); + assert_eq!(p.get_v().to_string(), v.hash_y); } } } diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index e475bd7ced..6487dd626b 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -1,6 +1,8 @@ //! Structs for core Zcash primitives. -use ff::{Field, PrimeField}; +use ff::PrimeField; +use group::{Curve, Group, GroupEncoding}; +use std::convert::TryInto; use crate::constants; @@ -10,8 +12,6 @@ use crate::pedersen_hash::{pedersen_hash, Personalization}; use byteorder::{LittleEndian, WriteBytesExt}; -use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform}; - use crate::keys::prf_expand; use blake2s_simd::Params as Blake2sParams; @@ -19,89 +19,66 @@ use blake2s_simd::Params as Blake2sParams; use rand_core::{CryptoRng, RngCore}; #[derive(Clone)] -pub struct ValueCommitment { +pub struct ValueCommitment { pub value: u64, - pub randomness: E::Fs, + pub randomness: jubjub::Fr, } -impl ValueCommitment { - pub fn cm(&self, params: &E::Params) -> edwards::Point { - params - .generator(FixedGenerators::ValueCommitmentValue) - .mul(E::Fs::from(self.value), params) - .add( - ¶ms - .generator(FixedGenerators::ValueCommitmentRandomness) - .mul(self.randomness, params), - params, - ) +impl ValueCommitment { + pub fn cm(&self) -> jubjub::SubgroupPoint { + (constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value)) + + (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness) } } #[derive(Clone)] -pub struct ProofGenerationKey { - pub ak: edwards::Point, - pub nsk: E::Fs, +pub struct ProofGenerationKey { + pub ak: jubjub::SubgroupPoint, + pub nsk: jubjub::Fr, } -impl ProofGenerationKey { - pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey { +impl ProofGenerationKey { + pub fn to_viewing_key(&self) -> ViewingKey { ViewingKey { ak: self.ak.clone(), - nk: params - .generator(FixedGenerators::ProofGenerationKey) - .mul(self.nsk, params), + nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk, } } } #[derive(Debug)] -pub struct ViewingKey { - pub ak: edwards::Point, - pub nk: edwards::Point, +pub struct ViewingKey { + pub ak: jubjub::SubgroupPoint, + pub nk: jubjub::SubgroupPoint, } -impl ViewingKey { - pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point { - self.ak.add( - ¶ms - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(ar, params), - params, - ) +impl ViewingKey { + pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint { + self.ak + constants::SPENDING_KEY_GENERATOR * ar } - 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(); - + pub fn ivk(&self) -> jubjub::Fr { let mut h = [0; 32]; h.copy_from_slice( Blake2sParams::new() .hash_length(32) .personal(constants::CRH_IVK_PERSONALIZATION) - .hash(&preimage) + .to_state() + .update(&self.ak.to_bytes()) + .update(&self.nk.to_bytes()) + .finalize() .as_bytes(), ); // Drop the most significant five bits, so it can be interpreted as a scalar. h[31] &= 0b0000_0111; - let mut e = ::Repr::default(); - e.as_mut().copy_from_slice(&h[..]); - - E::Fs::from_repr(e).expect("should be a valid scalar") + jubjub::Fr::from_repr(h).expect("should be a valid scalar") } - pub fn to_payment_address( - &self, - diversifier: Diversifier, - params: &E::Params, - ) -> Option> { - diversifier.g_d(params).and_then(|g_d| { - let pk_d = g_d.mul(self.ivk(), params); + pub fn to_payment_address(&self, diversifier: Diversifier) -> Option { + diversifier.g_d().and_then(|g_d| { + let pk_d = g_d * self.ivk(); PaymentAddress::from_parts(diversifier, pk_d) }) @@ -112,15 +89,8 @@ impl ViewingKey { pub struct Diversifier(pub [u8; 11]); impl Diversifier { - pub fn g_d( - &self, - params: &E::Params, - ) -> Option> { - group_hash::( - &self.0, - constants::KEY_DIVERSIFICATION_PERSONALIZATION, - params, - ) + pub fn g_d(&self) -> Option { + group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION) } } @@ -131,26 +101,23 @@ impl Diversifier { /// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub, /// and not the identity). #[derive(Clone, Debug)] -pub struct PaymentAddress { - pk_d: edwards::Point, +pub struct PaymentAddress { + pk_d: jubjub::SubgroupPoint, diversifier: Diversifier, } -impl PartialEq for PaymentAddress { +impl PartialEq for PaymentAddress { fn eq(&self, other: &Self) -> bool { self.pk_d == other.pk_d && self.diversifier == other.diversifier } } -impl PaymentAddress { +impl PaymentAddress { /// Constructs a PaymentAddress from a diversifier and a Jubjub point. /// /// Returns None if `pk_d` is the identity. - pub fn from_parts( - diversifier: Diversifier, - pk_d: edwards::Point, - ) -> Option { - if pk_d == edwards::Point::zero() { + pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option { + if pk_d.is_identity().into() { None } else { Some(PaymentAddress { pk_d, diversifier }) @@ -163,34 +130,36 @@ impl PaymentAddress { #[cfg(test)] pub(crate) fn from_parts_unchecked( diversifier: Diversifier, - pk_d: edwards::Point, + pk_d: jubjub::SubgroupPoint, ) -> Self { PaymentAddress { pk_d, diversifier } } /// Parses a PaymentAddress from bytes. - pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option { + pub fn from_bytes(bytes: &[u8; 43]) -> Option { let diversifier = { let mut tmp = [0; 11]; tmp.copy_from_slice(&bytes[0..11]); Diversifier(tmp) }; // Check that the diversifier is valid - if diversifier.g_d::(params).is_none() { + if diversifier.g_d().is_none() { return None; } - edwards::Point::::read(&bytes[11..43], params) - .ok()? - .as_prime_order(params) - .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d)) + let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap()); + if pk_d.is_some().into() { + PaymentAddress::from_parts(diversifier, pk_d.unwrap()) + } else { + None + } } /// Returns the byte encoding of this `PaymentAddress`. pub fn to_bytes(&self) -> [u8; 43] { let mut bytes = [0; 43]; bytes[0..11].copy_from_slice(&self.diversifier.0); - self.pk_d.write(&mut bytes[11..]).unwrap(); + bytes[11..].copy_from_slice(&self.pk_d.to_bytes()); bytes } @@ -200,21 +169,16 @@ impl PaymentAddress { } /// Returns `pk_d` for this `PaymentAddress`. - pub fn pk_d(&self) -> &edwards::Point { + pub fn pk_d(&self) -> &jubjub::SubgroupPoint { &self.pk_d } - pub fn g_d(&self, params: &E::Params) -> Option> { - self.diversifier.g_d(params) + pub fn g_d(&self) -> Option { + self.diversifier.g_d() } - pub fn create_note( - &self, - value: u64, - randomness: Rseed, - params: &E::Params, - ) -> Option> { - self.g_d(params).map(|g_d| Note { + pub fn create_note(&self, value: u64, randomness: Rseed) -> Option { + self.g_d().map(|g_d| Note { value, rseed: randomness, g_d, @@ -229,24 +193,24 @@ impl PaymentAddress { /// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive /// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`. #[derive(Copy, Clone, Debug)] -pub enum Rseed { - BeforeZip212(Fs), +pub enum Rseed { + BeforeZip212(jubjub::Fr), AfterZip212([u8; 32]), } #[derive(Clone, Debug)] -pub struct Note { +pub struct Note { /// The value of the note pub value: u64, /// The diversified base of the address, GH(d) - pub g_d: edwards::Point, + pub g_d: jubjub::SubgroupPoint, /// The public key of the address, g_d^ivk - pub pk_d: edwards::Point, + pub pk_d: jubjub::SubgroupPoint, /// rseed - pub rseed: Rseed, + pub rseed: Rseed, } -impl PartialEq for Note { +impl PartialEq for Note { fn eq(&self, other: &Self) -> bool { self.value == other.value && self.g_d == other.g_d @@ -255,18 +219,18 @@ impl PartialEq for Note { } } -impl Note { - pub fn uncommitted() -> E::Fr { +impl Note { + pub fn uncommitted() -> bls12_381::Scalar { // 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() + bls12_381::Scalar::one() } /// Computes the note commitment, returning the full point. - fn cm_full_point(&self, params: &E::Params) -> edwards::Point { + fn cm_full_point(&self) -> jubjub::SubgroupPoint { // Calculate the note contents, as bytes let mut note_contents = vec![]; @@ -276,10 +240,10 @@ impl Note { .unwrap(); // Write g_d - self.g_d.write(&mut note_contents).unwrap(); + note_contents.extend_from_slice(&self.g_d.to_bytes()); // Write pk_d - self.pk_d.write(&mut note_contents).unwrap(); + note_contents.extend_from_slice(&self.pk_d.to_bytes()); assert_eq!(note_contents.len(), 32 + 32 + 8); @@ -289,74 +253,70 @@ impl Note { 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.rcm(), params) - .add(&hash_of_contents, params) + (constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents } /// Computes the nullifier given the viewing key and /// note position - pub fn nf(&self, viewing_key: &ViewingKey, position: u64, params: &E::Params) -> Vec { + pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Vec { // Compute rho = cm + position.G - let rho = self.cm_full_point(params).add( - ¶ms - .generator(FixedGenerators::NullifierPosition) - .mul(E::Fs::from(position), params), - params, - ); + let rho = self.cm_full_point() + + (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position)); // 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) + .to_state() + .update(&viewing_key.nk.to_bytes()) + .update(&rho.to_bytes()) + .finalize() .as_bytes() .to_vec() } /// Computes the note commitment - pub fn cm(&self, params: &E::Params) -> E::Fr { + pub fn cm(&self) -> bls12_381::Scalar { // 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).to_xy().0 + // commitment to the u-coordinate is an injective encoding. + jubjub::ExtendedPoint::from(self.cm_full_point()) + .to_affine() + .get_u() } - pub fn rcm(&self) -> E::Fs { + pub fn rcm(&self) -> jubjub::Fr { match self.rseed { Rseed::BeforeZip212(rcm) => rcm, - Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x04]).as_bytes()), + Rseed::AfterZip212(rseed) => { + jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array()) + } } } - pub fn generate_or_derive_esk(&self, rng: &mut R) -> E::Fs { + pub fn generate_or_derive_esk(&self, rng: &mut R) -> jubjub::Fr { match self.derive_esk() { None => { // create random 64 byte buffer let mut buffer = [0u8; 64]; - &rng.fill_bytes(&mut buffer); + rng.fill_bytes(&mut buffer); // reduce to uniform value - E::Fs::to_uniform(&buffer[..]) + jubjub::Fr::from_bytes_wide(&buffer) } Some(esk) => esk, } } /// Returns the derived `esk` if this note was created after ZIP 212 activated. - pub fn derive_esk(&self) -> Option { + pub fn derive_esk(&self) -> Option { match self.rseed { Rseed::BeforeZip212(_) => None, - Rseed::AfterZip212(rseed) => { - Some(E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes())) - } + Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide( + prf_expand(&rseed, &[0x05]).as_array(), + )), } } } diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 2c40f8d562..1349d3b552 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -1,10 +1,6 @@ //! Abstractions over the proving system and parameters. -use crate::{ - jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, -}; -use pairing::bls12_381::{Bls12, Fr}; +use crate::primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}; use crate::{ merkle_tree::MerklePath, @@ -29,21 +25,14 @@ pub trait TxProver { fn spend_proof( &self, ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rseed: Rseed, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - >; + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()>; /// Create the value commitment and proof for a Sapling [`OutputDescription`], /// while accumulating its value commitment randomness inside the context for later @@ -53,11 +42,11 @@ pub trait TxProver { fn output_proof( &self, ctx: &mut Self::SaplingProvingContext, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point); + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint); /// Create the `bindingSig` for a Sapling transaction. All calls to /// [`TxProver::spend_proof`] and [`TxProver::output_proof`] must be completed before @@ -73,11 +62,10 @@ pub trait TxProver { #[cfg(test)] pub(crate) mod mock { use ff::Field; - use pairing::bls12_381::{Bls12, Fr}; use rand_core::OsRng; use crate::{ - jubjub::{edwards, fs::Fs, FixedGenerators, Unknown}, + constants::SPENDING_KEY_GENERATOR, primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, }; @@ -86,7 +74,6 @@ pub(crate) mod mock { redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, - JUBJUB, }; use super::TxProver; @@ -102,35 +89,25 @@ pub(crate) mod mock { fn spend_proof( &self, _ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, _diversifier: Diversifier, - _rcm: Rseed, - ar: Fs, + _rcm: Rseed, + ar: jubjub::Fr, value: u64, - _anchor: Fr, + _anchor: bls12_381::Scalar, _merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { let mut rng = OsRng; - let cv = ValueCommitment:: { + let cv = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), } - .cm(&JUBJUB) + .cm() .into(); - let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( - ar, - FixedGenerators::SpendingKeyGenerator, - &JUBJUB, - ); + let rk = PublicKey(proof_generation_key.ak.clone().into()) + .randomize(ar, SPENDING_KEY_GENERATOR); Ok(([0u8; GROTH_PROOF_SIZE], cv, rk)) } @@ -138,18 +115,18 @@ pub(crate) mod mock { fn output_proof( &self, _ctx: &mut Self::SaplingProvingContext, - _esk: Fs, - _payment_address: PaymentAddress, - _rcm: Fs, + _esk: jubjub::Fr, + _payment_address: PaymentAddress, + _rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { let mut rng = OsRng; - let cv = ValueCommitment:: { + let cv = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), } - .cm(&JUBJUB) + .cm() .into(); ([0u8; GROTH_PROOF_SIZE], cv) diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index c8088f62de..571f7b9b32 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -3,28 +3,29 @@ //! //! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa -use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown}; use ff::{Field, PrimeField}; +use group::GroupEncoding; +use jubjub::{ExtendedPoint, SubgroupPoint}; use rand_core::RngCore; use std::io::{self, Read, Write}; use std::ops::{AddAssign, MulAssign, Neg}; use crate::util::hash_to_scalar; -fn read_scalar(mut reader: R) -> io::Result { - let mut s_repr = ::Repr::default(); +fn read_scalar(mut reader: R) -> io::Result { + let mut s_repr = [0; 32]; reader.read_exact(s_repr.as_mut())?; - E::Fs::from_repr(s_repr) + jubjub::Fr::from_repr(s_repr) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) } -fn write_scalar(s: &E::Fs, mut writer: W) -> io::Result<()> { +fn write_scalar(s: &jubjub::Fr, mut writer: W) -> io::Result<()> { writer.write_all(s.to_repr().as_ref()) } -fn h_star(a: &[u8], b: &[u8]) -> E::Fs { - hash_to_scalar::(b"Zcash_RedJubjubH", a, b) +fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr { + hash_to_scalar(b"Zcash_RedJubjubH", a, b) } #[derive(Copy, Clone, Debug)] @@ -33,10 +34,10 @@ pub struct Signature { sbar: [u8; 32], } -pub struct PrivateKey(pub E::Fs); +pub struct PrivateKey(pub jubjub::Fr); #[derive(Debug)] -pub struct PublicKey(pub Point); +pub struct PublicKey(pub ExtendedPoint); impl Signature { pub fn read(mut reader: R) -> io::Result { @@ -53,167 +54,156 @@ impl Signature { } } -impl PrivateKey { - pub fn randomize(&self, alpha: E::Fs) -> Self { +impl PrivateKey { + pub fn randomize(&self, alpha: jubjub::Fr) -> Self { let mut tmp = self.0; tmp.add_assign(&alpha); PrivateKey(tmp) } pub fn read(reader: R) -> io::Result { - let pk = read_scalar::(reader)?; + let pk = read_scalar::(reader)?; Ok(PrivateKey(pk)) } pub fn write(&self, writer: W) -> io::Result<()> { - write_scalar::(&self.0, writer) + write_scalar::(&self.0, writer) } - pub fn sign( - &self, - msg: &[u8], - rng: &mut R, - p_g: FixedGenerators, - params: &E::Params, - ) -> Signature { + pub fn sign(&self, msg: &[u8], rng: &mut R, p_g: SubgroupPoint) -> Signature { // T = (l_H + 128) bits of randomness // For H*, l_H = 512 bits let mut t = [0u8; 80]; rng.fill_bytes(&mut t[..]); // r = H*(T || M) - let r = h_star::(&t[..], msg); + let r = h_star(&t[..], msg); // R = r . P_G - let r_g = params.generator(p_g).mul(r, params); - let mut rbar = [0u8; 32]; - r_g.write(&mut rbar[..]) - .expect("Jubjub points should serialize to 32 bytes"); + let r_g = p_g * r; + let rbar = r_g.to_bytes(); // S = r + H*(Rbar || M) . sk - let mut s = h_star::(&rbar[..], msg); + let mut s = h_star(&rbar[..], msg); s.mul_assign(&self.0); s.add_assign(&r); let mut sbar = [0u8; 32]; - write_scalar::(&s, &mut sbar[..]) + write_scalar::<&mut [u8]>(&s, &mut sbar[..]) .expect("Jubjub scalars should serialize to 32 bytes"); Signature { rbar, sbar } } } -impl PublicKey { - pub fn from_private(privkey: &PrivateKey, p_g: FixedGenerators, params: &E::Params) -> Self { - let res = params.generator(p_g).mul(privkey.0, params).into(); - PublicKey(res) +impl PublicKey { + pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self { + PublicKey((p_g * privkey.0).into()) } - pub fn randomize(&self, alpha: E::Fs, p_g: FixedGenerators, params: &E::Params) -> Self { - let res: Point = params.generator(p_g).mul(alpha, params).into(); - let res = res.add(&self.0, params); - PublicKey(res) + pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self { + PublicKey(ExtendedPoint::from(p_g * alpha) + self.0) } - pub fn read(reader: R, params: &E::Params) -> io::Result { - let p = Point::read(reader, params)?; - Ok(PublicKey(p)) + pub fn read(mut reader: R) -> io::Result { + let mut bytes = [0; 32]; + reader.read_exact(&mut bytes)?; + let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey); + if p.is_some().into() { + Ok(p.unwrap()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid RedJubjub public key", + )) + } } - pub fn write(&self, writer: W) -> io::Result<()> { - self.0.write(writer) + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.0.to_bytes()) } - pub fn verify( - &self, - msg: &[u8], - sig: &Signature, - p_g: FixedGenerators, - params: &E::Params, - ) -> bool { + pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool { // c = H*(Rbar || M) - let c = h_star::(&sig.rbar[..], msg); + let c = h_star(&sig.rbar[..], msg); // Signature checks: // R != invalid - let r = match Point::read(&sig.rbar[..], params) { - Ok(r) => r, - Err(_) => return false, + let r = { + let r = ExtendedPoint::from_bytes(&sig.rbar); + if r.is_none().into() { + return false; + } + r.unwrap() }; // S < order(G) - // (E::Fs guarantees its representation is in the field) - let s = match read_scalar::(&sig.sbar[..]) { + // (jubjub::Scalar guarantees its representation is in the field) + let s = match read_scalar::<&[u8]>(&sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; // 0 = h_G(-S . P_G + R + c . vk) - self.0 - .mul(c, params) - .add(&r, params) - .add( - ¶ms.generator(p_g).mul(s, params).negate().into(), - params, - ) - .mul_by_cofactor(params) - .eq(&Point::zero()) + ((self.0 * c) + r - (p_g * s)) + .mul_by_cofactor() + .is_identity() + .into() } } -pub struct BatchEntry<'a, E: JubjubEngine> { - vk: PublicKey, +pub struct BatchEntry<'a> { + vk: PublicKey, msg: &'a [u8], sig: Signature, } // TODO: #82: This is a naive implementation currently, // and doesn't use multiexp. -pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>( +pub fn batch_verify<'a, R: RngCore>( rng: &mut R, - batch: &[BatchEntry<'a, E>], - p_g: FixedGenerators, - params: &E::Params, + batch: &[BatchEntry<'a>], + p_g: SubgroupPoint, ) -> bool { - let mut acc = Point::::zero(); + let mut acc = ExtendedPoint::identity(); for entry in batch { - let mut r = match Point::::read(&entry.sig.rbar[..], params) { - Ok(r) => r, - Err(_) => return false, + let mut r = { + let r = ExtendedPoint::from_bytes(&entry.sig.rbar); + if r.is_none().into() { + return false; + } + r.unwrap() }; - let mut s = match read_scalar::(&entry.sig.sbar[..]) { + let mut s = match read_scalar::<&[u8]>(&entry.sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; - let mut c = h_star::(&entry.sig.rbar[..], entry.msg); + let mut c = h_star(&entry.sig.rbar[..], entry.msg); - let z = E::Fs::random(rng); + let z = jubjub::Fr::random(rng); s.mul_assign(&z); s = s.neg(); - r = r.mul(z, params); + r = r * z; c.mul_assign(&z); - acc = acc.add(&r, params); - acc = acc.add(&entry.vk.0.mul(c, params), params); - acc = acc.add(¶ms.generator(p_g).mul(s, params).into(), params); + acc = acc + r + (&entry.vk.0 * c) + (p_g * s); } - acc = acc.mul_by_cofactor(params).into(); + acc = acc.mul_by_cofactor().into(); - acc.eq(&Point::zero()) + acc.is_identity().into() } #[cfg(test)] mod tests { - use pairing::bls12_381::Bls12; + use group::Group; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; - use crate::jubjub::{edwards, fs::Fs, JubjubBls12}; - use super::*; + use crate::constants::SPENDING_KEY_GENERATOR; #[test] fn test_batch_verify() { @@ -221,20 +211,19 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); - let p_g = FixedGenerators::SpendingKeyGenerator; + let p_g = SPENDING_KEY_GENERATOR; - let sk1 = PrivateKey::(Fs::random(rng)); - let vk1 = PublicKey::from_private(&sk1, p_g, params); + let sk1 = PrivateKey(jubjub::Fr::random(rng)); + let vk1 = PublicKey::from_private(&sk1, p_g); let msg1 = b"Foo bar"; - let sig1 = sk1.sign(msg1, rng, p_g, params); - assert!(vk1.verify(msg1, &sig1, p_g, params)); + let sig1 = sk1.sign(msg1, rng, p_g); + assert!(vk1.verify(msg1, &sig1, p_g)); - let sk2 = PrivateKey::(Fs::random(rng)); - let vk2 = PublicKey::from_private(&sk2, p_g, params); + let sk2 = PrivateKey(jubjub::Fr::random(rng)); + let vk2 = PublicKey::from_private(&sk2, p_g); let msg2 = b"Foo bar"; - let sig2 = sk2.sign(msg2, rng, p_g, params); - assert!(vk2.verify(msg2, &sig2, p_g, params)); + let sig2 = sk2.sign(msg2, rng, p_g); + assert!(vk2.verify(msg2, &sig2, p_g)); let mut batch = vec![ BatchEntry { @@ -249,11 +238,11 @@ mod tests { }, ]; - assert!(batch_verify(rng, &batch, p_g, params)); + assert!(batch_verify(rng, &batch, p_g)); batch[0].sig = sig2; - assert!(!batch_verify(rng, &batch, p_g, params)); + assert!(!batch_verify(rng, &batch, p_g)); } #[test] @@ -262,33 +251,34 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); - let zero = edwards::Point::zero(); - let p_g = FixedGenerators::SpendingKeyGenerator; + let zero = jubjub::ExtendedPoint::identity(); + let p_g = SPENDING_KEY_GENERATOR; // Get a point of order 8 let p8 = loop { - let r = edwards::Point::::rand(rng, params).mul(Fs::char(), params); + let r = jubjub::ExtendedPoint::random(rng) + .to_niels() + .multiply_bits(&jubjub::Fr::char()); - let r2 = r.double(params); - let r4 = r2.double(params); - let r8 = r4.double(params); + let r2 = r.double(); + let r4 = r2.double(); + let r8 = r4.double(); if r2 != zero && r4 != zero && r8 == zero { break r; } }; - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); // TODO: This test will need to change when #77 is fixed let msg = b"Foo bar"; - let sig = sk.sign(msg, rng, p_g, params); - assert!(vk.verify(msg, &sig, p_g, params)); + let sig = sk.sign(msg, rng, p_g); + assert!(vk.verify(msg, &sig, p_g)); - let vktorsion = PublicKey(vk.0.add(&p8, params)); - assert!(vktorsion.verify(msg, &sig, p_g, params)); + let vktorsion = PublicKey(vk.0 + p8); + assert!(vktorsion.verify(msg, &sig, p_g)); } #[test] @@ -297,14 +287,13 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let p_g = FixedGenerators::SpendingKeyGenerator; - let params = &JubjubBls12::new(); + let p_g = SPENDING_KEY_GENERATOR; for _ in 0..1000 { - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); let msg = b"Foo bar"; - let sig = sk.sign(msg, rng, p_g, params); + let sig = sk.sign(msg, rng, p_g); let mut sk_bytes = [0u8; 32]; let mut vk_bytes = [0u8; 32]; @@ -313,17 +302,17 @@ mod tests { vk.write(&mut vk_bytes[..]).unwrap(); sig.write(&mut sig_bytes[..]).unwrap(); - let sk_2 = PrivateKey::::read(&sk_bytes[..]).unwrap(); - let vk_2 = PublicKey::from_private(&sk_2, p_g, params); + let sk_2 = PrivateKey::read(&sk_bytes[..]).unwrap(); + let vk_2 = PublicKey::from_private(&sk_2, p_g); let mut vk_2_bytes = [0u8; 32]; vk_2.write(&mut vk_2_bytes[..]).unwrap(); assert!(vk_bytes == vk_2_bytes); - let vk_2 = PublicKey::::read(&vk_bytes[..], params).unwrap(); + let vk_2 = PublicKey::read(&vk_bytes[..]).unwrap(); let sig_2 = Signature::read(&sig_bytes[..]).unwrap(); - assert!(vk.verify(msg, &sig_2, p_g, params)); - assert!(vk_2.verify(msg, &sig, p_g, params)); - assert!(vk_2.verify(msg, &sig_2, p_g, params)); + assert!(vk.verify(msg, &sig_2, p_g)); + assert!(vk_2.verify(msg, &sig, p_g)); + assert!(vk_2.verify(msg, &sig_2, p_g)); } } @@ -333,35 +322,34 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let p_g = FixedGenerators::SpendingKeyGenerator; - let params = &JubjubBls12::new(); + let p_g = SPENDING_KEY_GENERATOR; for _ in 0..1000 { - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); let msg1 = b"Foo bar"; let msg2 = b"Spam eggs"; - let sig1 = sk.sign(msg1, rng, p_g, params); - let sig2 = sk.sign(msg2, rng, p_g, params); + let sig1 = sk.sign(msg1, rng, p_g); + let sig2 = sk.sign(msg2, rng, p_g); - assert!(vk.verify(msg1, &sig1, p_g, params)); - assert!(vk.verify(msg2, &sig2, p_g, params)); - assert!(!vk.verify(msg1, &sig2, p_g, params)); - assert!(!vk.verify(msg2, &sig1, p_g, params)); + assert!(vk.verify(msg1, &sig1, p_g)); + assert!(vk.verify(msg2, &sig2, p_g)); + assert!(!vk.verify(msg1, &sig2, p_g)); + assert!(!vk.verify(msg2, &sig1, p_g)); - let alpha = Fs::random(rng); + let alpha = jubjub::Fr::random(rng); let rsk = sk.randomize(alpha); - let rvk = vk.randomize(alpha, p_g, params); + let rvk = vk.randomize(alpha, p_g); - let sig1 = rsk.sign(msg1, rng, p_g, params); - let sig2 = rsk.sign(msg2, rng, p_g, params); + let sig1 = rsk.sign(msg1, rng, p_g); + let sig2 = rsk.sign(msg2, rng, p_g); - assert!(rvk.verify(msg1, &sig1, p_g, params)); - assert!(rvk.verify(msg2, &sig2, p_g, params)); - assert!(!rvk.verify(msg1, &sig2, p_g, params)); - assert!(!rvk.verify(msg2, &sig1, p_g, params)); + assert!(rvk.verify(msg1, &sig1, p_g)); + assert!(rvk.verify(msg2, &sig2, p_g)); + assert!(!rvk.verify(msg1, &sig2, p_g)); + assert!(!rvk.verify(msg2, &sig1, p_g)); } } } diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 0251803078..7b007edfc0 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -1,24 +1,23 @@ //! Structs and constants specific to the Sapling shielded pool. use crate::{ - jubjub::{fs::Fs, FixedGenerators, JubjubBls12}, + constants::SPENDING_KEY_GENERATOR, pedersen_hash::{pedersen_hash, Personalization}, primitives::Note, }; use ff::{BitIterator, PrimeField}; +use group::{Curve, GroupEncoding}; use lazy_static::lazy_static; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; use rand_core::{CryptoRng, RngCore}; use std::io::{self, Read, Write}; use crate::merkle_tree::Hashable; use crate::redjubjub::{PrivateKey, PublicKey, Signature}; -use crate::JUBJUB; pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; /// Compute a parent node in the Sapling commitment tree given its two children. -pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { +pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] { let lhs = { let mut tmp = [false; 256]; for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { @@ -35,35 +34,38 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { tmp }; - pedersen_hash::( + jubjub::ExtendedPoint::from(pedersen_hash( Personalization::MerkleTree(depth), lhs.iter() .copied() - .take(Fr::NUM_BITS as usize) - .chain(rhs.iter().copied().take(Fr::NUM_BITS as usize)), - &JUBJUB, - ) - .to_xy() - .0 + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain( + rhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), + )) + .to_affine() + .get_u() .to_repr() } /// A node within the Sapling commitment tree. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Node { - repr: FrRepr, + repr: [u8; 32], } impl Node { - pub fn new(repr: FrRepr) -> Self { + pub fn new(repr: [u8; 32]) -> Self { Node { repr } } } impl Hashable for Node { fn read(mut reader: R) -> io::Result { - let mut repr = FrRepr([0; 32]); - reader.read_exact(&mut repr.0)?; + let mut repr = [0; 32]; + reader.read_exact(&mut repr)?; Ok(Node::new(repr)) } @@ -79,7 +81,7 @@ impl Hashable for Node { fn blank() -> Self { Node { - repr: Note::::uncommitted().to_repr(), + repr: Note::uncommitted().to_repr(), } } @@ -88,9 +90,9 @@ impl Hashable for Node { } } -impl From for Fr { +impl From for bls12_381::Scalar { fn from(node: Node) -> Self { - Fr::from_repr(node.repr).expect("Tree nodes should be in the prime field") + bls12_381::Scalar::from_repr(node.repr).expect("Tree nodes should be in the prime field") } } @@ -107,29 +109,22 @@ lazy_static! { /// Create the spendAuthSig for a Sapling SpendDescription. pub fn spend_sig( - ask: PrivateKey, - ar: Fs, + ask: PrivateKey, + ar: jubjub::Fr, sighash: &[u8; 32], rng: &mut R, - params: &JubjubBls12, ) -> Signature { // We compute `rsk`... let rsk = ask.randomize(ar); // We compute `rk` from there (needed for key prefixing) - let rk = PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, params); + let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR); // Compute the signature's message for rk/spend_auth_sig let mut data_to_be_signed = [0u8; 64]; - rk.0.write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]); // Do the signing - rsk.sign( - &data_to_be_signed, - rng, - FixedGenerators::SpendingKeyGenerator, - params, - ) + rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR) } diff --git a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs index ba4f2a83eb..eb71175cf4 100644 --- a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs +++ b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs @@ -7,26 +7,26 @@ pub fn get_vectors<'a>() -> Vec> { TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1], - hash_x: "Fr(0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b)", - hash_y: "Fr(0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982)", + hash_x: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b", + hash_y: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 0], - hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)", - hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)", + hash_x: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a", + hash_y: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1], - hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", - hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0], - hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", - hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, @@ -39,8 +39,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, ], - hash_x: "Fr(0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f)", - hash_y: "Fr(0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527)", + hash_x: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f", + hash_y: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527", }, TestVector { personalization: Personalization::NoteCommitment, @@ -53,8 +53,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "Fr(0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd)", - hash_y: "Fr(0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2)", + hash_x: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd", + hash_y: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2", }, TestVector { personalization: Personalization::NoteCommitment, @@ -67,8 +67,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, ], - hash_x: "Fr(0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40)", - hash_y: "Fr(0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743)", + hash_x: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40", + hash_y: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743", }, TestVector { personalization: Personalization::NoteCommitment, @@ -101,8 +101,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, ], - hash_x: "Fr(0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27)", - hash_y: "Fr(0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74)", + hash_x: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27", + hash_y: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74", }, TestVector { personalization: Personalization::NoteCommitment, @@ -136,8 +136,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, ], - hash_x: "Fr(0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e)", - hash_y: "Fr(0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab)", + hash_x: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e", + hash_y: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab", }, TestVector { personalization: Personalization::NoteCommitment, @@ -177,8 +177,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, ], - hash_x: "Fr(0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f)", - hash_y: "Fr(0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71)", + hash_x: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f", + hash_y: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71", }, TestVector { personalization: Personalization::NoteCommitment, @@ -218,32 +218,32 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, ], - hash_x: "Fr(0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7)", - hash_y: "Fr(0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f)", + hash_x: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7", + hash_y: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0], - hash_x: "Fr(0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d)", - hash_y: "Fr(0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330)", + hash_x: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d", + hash_y: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 0], - hash_x: "Fr(0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea)", - hash_y: "Fr(0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4)", + hash_x: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea", + hash_y: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1], - hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", - hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0], - hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", - hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -256,8 +256,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd)", - hash_y: "Fr(0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544)", + hash_x: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd", + hash_y: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -270,8 +270,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, ], - hash_x: "Fr(0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555)", - hash_y: "Fr(0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb)", + hash_x: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555", + hash_y: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -284,8 +284,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, ], - hash_x: "Fr(0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d)", - hash_y: "Fr(0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f)", + hash_x: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d", + hash_y: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -318,8 +318,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, ], - hash_x: "Fr(0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85)", - hash_y: "Fr(0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a)", + hash_x: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85", + hash_y: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -353,8 +353,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, ], - hash_x: "Fr(0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68)", - hash_y: "Fr(0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee)", + hash_x: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68", + hash_y: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -394,8 +394,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37)", - hash_y: "Fr(0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6)", + hash_x: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37", + hash_y: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -435,32 +435,32 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "Fr(0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05)", - hash_y: "Fr(0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33)", + hash_x: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05", + hash_y: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1], - hash_x: "Fr(0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a)", - hash_y: "Fr(0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a)", + hash_x: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a", + hash_y: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 0], - hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)", - hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)", + hash_x: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c", + hash_y: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1], - hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", - hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0], - hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", - hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -473,8 +473,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2)", - hash_y: "Fr(0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31)", + hash_x: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2", + hash_y: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -487,8 +487,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, ], - hash_x: "Fr(0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb)", - hash_y: "Fr(0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f)", + hash_x: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb", + hash_y: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -501,8 +501,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, ], - hash_x: "Fr(0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2)", - hash_y: "Fr(0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de)", + hash_x: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2", + hash_y: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -535,8 +535,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, ], - hash_x: "Fr(0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc)", - hash_y: "Fr(0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de)", + hash_x: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc", + hash_y: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -570,8 +570,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, ], - hash_x: "Fr(0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda)", - hash_y: "Fr(0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d)", + hash_x: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda", + hash_y: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -611,8 +611,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, ], - hash_x: "Fr(0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666)", - hash_y: "Fr(0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289)", + hash_x: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666", + hash_y: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -652,8 +652,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, ], - hash_x: "Fr(0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7)", - hash_y: "Fr(0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad)", + hash_x: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7", + hash_y: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad", }, TestVector { personalization: Personalization::MerkleTree(27), @@ -666,8 +666,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, ], - hash_x: "Fr(0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4)", - hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + hash_x: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4", + hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, TestVector { personalization: Personalization::MerkleTree(36), @@ -680,8 +680,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, ], - hash_x: "Fr(0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024)", - hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + hash_x: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024", + hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -694,8 +694,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - hash_x: "Fr(0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd)", - hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + hash_x: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd", + hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::NoteCommitment, @@ -708,8 +708,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ], - hash_x: "Fr(0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d)", - hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + hash_x: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d", + hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, ]; } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9287442767..9bc3577db2 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -1,12 +1,8 @@ //! Structs for building transactions. +use crate::primitives::{Diversifier, Note, PaymentAddress}; use crate::zip32::ExtendedSpendingKey; -use crate::{ - jubjub::fs::Fs, - primitives::{Diversifier, Note, PaymentAddress}, -}; use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; use std::error; use std::fmt; @@ -26,7 +22,6 @@ use crate::{ signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, util::generate_random_rseed, - JUBJUB, }; #[cfg(feature = "transparent-inputs")] @@ -75,15 +70,15 @@ impl error::Error for Error {} struct SpendDescriptionInfo { extsk: ExtendedSpendingKey, diversifier: Diversifier, - note: Note, - alpha: Fs, + note: Note, + alpha: jubjub::Fr, merkle_path: MerklePath, } pub struct SaplingOutput { ovk: OutgoingViewingKey, - to: PaymentAddress, - note: Note, + to: PaymentAddress, + note: Note, memo: Memo, } @@ -92,11 +87,11 @@ impl SaplingOutput { height: u32, rng: &mut R, ovk: OutgoingViewingKey, - to: PaymentAddress, + to: PaymentAddress, value: Amount, memo: Option, ) -> Result { - let g_d = match to.g_d(&JUBJUB) { + let g_d = match to.g_d() { Some(g_d) => g_d, None => return Err(Error::InvalidAddress), }; @@ -143,7 +138,7 @@ impl SaplingOutput { self.note.value, ); - let cmu = self.note.cm(&JUBJUB); + let cmu = self.note.cm(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu); @@ -310,11 +305,11 @@ pub struct Builder { height: u32, mtx: TransactionData, fee: Amount, - anchor: Option, + anchor: Option, spends: Vec, outputs: Vec, transparent_inputs: TransparentInputs, - change_address: Option<(OutgoingViewingKey, PaymentAddress)>, + change_address: Option<(OutgoingViewingKey, PaymentAddress)>, phantom: PhantomData

, } @@ -369,13 +364,13 @@ impl Builder { &mut self, extsk: ExtendedSpendingKey, diversifier: Diversifier, - note: Note, + note: Note, merkle_path: MerklePath, ) -> Result<(), Error> { // Consistency check: all anchors must equal the first one - let cm = Node::new(note.cm(&JUBJUB).into()); + let cm = Node::new(note.cm().into()); if let Some(anchor) = self.anchor { - let path_root: Fr = merkle_path.root(cm).into(); + let path_root: bls12_381::Scalar = merkle_path.root(cm).into(); if path_root != anchor { return Err(Error::AnchorMismatch); } @@ -383,7 +378,7 @@ impl Builder { self.anchor = Some(merkle_path.root(cm).into()) } - let alpha = Fs::random(&mut self.rng); + let alpha = jubjub::Fr::random(&mut self.rng); self.mtx.value_balance += Amount::from_u64(note.value).map_err(|_| Error::InvalidAmount)?; @@ -402,7 +397,7 @@ impl Builder { pub fn add_sapling_output( &mut self, ovk: OutgoingViewingKey, - to: PaymentAddress, + to: PaymentAddress, value: Amount, memo: Option, ) -> Result<(), Error> { @@ -448,7 +443,7 @@ impl Builder { /// /// By default, change is sent to the Sapling address corresponding to the first note /// being spent (i.e. the first call to [`Builder::add_sapling_spend`]). - pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) { + pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) { self.change_address = Some((ovk, to)); } @@ -548,13 +543,12 @@ impl Builder { let anchor = self.anchor.expect("anchor was set if spends were added"); for (i, (pos, spend)) in spends.iter().enumerate() { - let proof_generation_key = spend.extsk.expsk.proof_generation_key(&JUBJUB); + let proof_generation_key = spend.extsk.expsk.proof_generation_key(); let mut nullifier = [0u8; 32]; nullifier.copy_from_slice(&spend.note.nf( - &proof_generation_key.to_viewing_key(&JUBJUB), + &proof_generation_key.to_viewing_key(), spend.merkle_path.position, - &JUBJUB, )); let (zkproof, cv, rk) = prover @@ -601,7 +595,7 @@ impl Builder { let mut d = [0; 11]; self.rng.fill_bytes(&mut d); diversifier = Diversifier(d); - if let Some(val) = diversifier.g_d::(&JUBJUB) { + if let Some(val) = diversifier.g_d() { g_d = val; break; } @@ -610,8 +604,8 @@ impl Builder { }; let (pk_d, payment_address) = loop { - let dummy_ivk = Fs::random(&mut self.rng); - let pk_d = g_d.mul(dummy_ivk, &JUBJUB); + let dummy_ivk = jubjub::Fr::random(&mut self.rng); + let pk_d = g_d * dummy_ivk; if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) { break (pk_d, addr); } @@ -631,7 +625,7 @@ impl Builder { }; let esk = dummy_note.generate_or_derive_esk(&mut self.rng); - let epk = dummy_note.g_d.mul(esk, &JUBJUB); + let epk = dummy_note.g_d * esk; let (zkproof, cv) = prover.output_proof( &mut ctx, @@ -641,7 +635,7 @@ impl Builder { dummy_note.value, ); - let cmu = dummy_note.cm(&JUBJUB); + let cmu = dummy_note.cm(); let mut enc_ciphertext = [0u8; 580]; let mut out_ciphertext = [0u8; 80]; @@ -680,7 +674,6 @@ impl Builder { spend.alpha, &sighash, &mut self.rng, - &JUBJUB, )); } @@ -712,8 +705,6 @@ mod tests { use rand_core::OsRng; use std::marker::PhantomData; - use crate::jubjub::fs::Fs; - use super::{Builder, Error}; use crate::{ consensus, @@ -725,7 +716,6 @@ mod tests { sapling::Node, transaction::components::Amount, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; #[test] @@ -788,9 +778,9 @@ mod tests { let mut rng = OsRng; let note1 = to - .create_note(50000, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) + .create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); + let cm1 = Node::new(note1.cm().to_repr()); let mut tree = CommitmentTree::new(); tree.append(cm1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); @@ -887,9 +877,9 @@ mod tests { } let note1 = to - .create_note(59999, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) + .create_note(59999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); + let cm1 = Node::new(note1.cm().to_repr()); let mut tree = CommitmentTree::new(); tree.append(cm1).unwrap(); let mut witness1 = IncrementalWitness::from_tree(&tree); @@ -927,9 +917,9 @@ mod tests { } let note2 = to - .create_note(1, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB) + .create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm2 = Node::new(note2.cm(&JUBJUB).to_repr()); + let cm2 = Node::new(note2.cm().to_repr()); tree.append(cm2).unwrap(); witness1.append(cm2).unwrap(); let witness2 = IncrementalWitness::from_tree(&tree); diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index abcffcb8f8..70e115a4fd 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -1,14 +1,12 @@ //! Structs representing the components within Zcash transactions. -use crate::jubjub::{edwards, Unknown}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::PrimeField; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; +use group::GroupEncoding; use std::io::{self, Read, Write}; use crate::legacy::Script; use crate::redjubjub::{PublicKey, Signature}; -use crate::JUBJUB; pub mod amount; pub use self::amount::Amount; @@ -118,10 +116,10 @@ impl TxOut { } pub struct SpendDescription { - pub cv: edwards::Point, - pub anchor: Fr, + pub cv: jubjub::ExtendedPoint, + pub anchor: bls12_381::Scalar, pub nullifier: [u8; 32], - pub rk: PublicKey, + pub rk: PublicKey, pub zkproof: [u8; GROTH_PROOF_SIZE], pub spend_auth_sig: Option, } @@ -142,13 +140,21 @@ impl SpendDescription { // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_spend() // (located in zcash_proofs::sapling::verifier). - let cv = edwards::Point::::read(&mut reader, &JUBJUB)?; + let cv = { + let mut bytes = [0; 32]; + reader.read_exact(&mut bytes)?; + let cv = jubjub::ExtendedPoint::from_bytes(&bytes); + if cv.is_none().into() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv")); + } + cv.unwrap() + }; // Consensus rule (§7.3): Canonical encoding is enforced here let anchor = { - let mut f = FrRepr([0; 32]); - reader.read_exact(&mut f.0)?; - Fr::from_repr(f) + let mut f = [0; 32]; + reader.read_exact(&mut f)?; + bls12_381::Scalar::from_repr(f) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))? }; @@ -158,7 +164,7 @@ impl SpendDescription { // Consensus rules (§4.4): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_spend() - let rk = PublicKey::::read(&mut reader, &JUBJUB)?; + let rk = PublicKey::read(&mut reader)?; // Consensus rules (§4.4): // - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend() @@ -183,7 +189,7 @@ impl SpendDescription { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.cv.write(&mut writer)?; + writer.write_all(&self.cv.to_bytes())?; writer.write_all(self.anchor.to_repr().as_ref())?; writer.write_all(&self.nullifier)?; self.rk.write(&mut writer)?; @@ -199,9 +205,9 @@ impl SpendDescription { } pub struct OutputDescription { - pub cv: edwards::Point, - pub cmu: Fr, - pub ephemeral_key: edwards::Point, + pub cv: jubjub::ExtendedPoint, + pub cmu: bls12_381::Scalar, + pub ephemeral_key: jubjub::ExtendedPoint, pub enc_ciphertext: [u8; 580], pub out_ciphertext: [u8; 80], pub zkproof: [u8; GROTH_PROOF_SIZE], @@ -218,25 +224,44 @@ impl std::fmt::Debug for OutputDescription { } impl OutputDescription { - pub fn read(mut reader: &mut R) -> io::Result { + pub fn read(reader: &mut R) -> io::Result { // Consensus rules (§4.5): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_output() // (located in zcash_proofs::sapling::verifier). - let cv = edwards::Point::::read(&mut reader, &JUBJUB)?; + let cv = { + let mut bytes = [0; 32]; + reader.read_exact(&mut bytes)?; + let cv = jubjub::ExtendedPoint::from_bytes(&bytes); + if cv.is_none().into() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv")); + } + cv.unwrap() + }; // Consensus rule (§7.4): Canonical encoding is enforced here let cmu = { - let mut f = FrRepr([0; 32]); - reader.read_exact(&mut f.0)?; - Fr::from_repr(f) + let mut f = [0; 32]; + reader.read_exact(&mut f)?; + bls12_381::Scalar::from_repr(f) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))? }; // Consensus rules (§4.5): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_output() - let ephemeral_key = edwards::Point::::read(&mut reader, &JUBJUB)?; + let ephemeral_key = { + let mut bytes = [0; 32]; + reader.read_exact(&mut bytes)?; + let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes); + if ephemeral_key.is_none().into() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid ephemeral_key", + )); + } + ephemeral_key.unwrap() + }; let mut enc_ciphertext = [0; 580]; let mut out_ciphertext = [0; 80]; @@ -261,9 +286,9 @@ impl OutputDescription { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.cv.write(&mut writer)?; + writer.write_all(&self.cv.to_bytes())?; writer.write_all(self.cmu.to_repr().as_ref())?; - self.ephemeral_key.write(&mut writer)?; + writer.write_all(&self.ephemeral_key.to_bytes())?; writer.write_all(&self.enc_ciphertext)?; writer.write_all(&self.out_ciphertext)?; writer.write_all(&self.zkproof) diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 89ee19270a..2cc847b59e 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -1,6 +1,7 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; use ff::PrimeField; +use group::GroupEncoding; use super::{ components::{Amount, TxOut}, @@ -127,7 +128,7 @@ fn joinsplits_hash(tx: &TransactionData) -> Blake2bHash { fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { - s_spend.cv.write(&mut data).unwrap(); + data.extend_from_slice(&s_spend.cv.to_bytes()); data.extend_from_slice(s_spend.anchor.to_repr().as_ref()); data.extend_from_slice(&s_spend.nullifier); s_spend.rk.write(&mut data).unwrap(); diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index 2fc267c6f2..a9c89776d8 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -1,12 +1,8 @@ use ff::Field; -use pairing::bls12_381::Bls12; use rand_core::OsRng; -use crate::jubjub::{fs::Fs, FixedGenerators}; - use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData}; -use crate::redjubjub::PrivateKey; -use crate::JUBJUB; +use crate::{constants::SPENDING_KEY_GENERATOR, redjubjub::PrivateKey}; #[test] fn tx_read_write() { @@ -56,13 +52,8 @@ fn tx_write_rejects_unexpected_binding_sig() { // Fails with an unexpected binding signature { let rng = &mut OsRng; - let sk = PrivateKey::(Fs::random(rng)); - let sig = sk.sign( - b"Foo bar", - rng, - FixedGenerators::SpendingKeyGenerator, - &JUBJUB, - ); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let sig = sk.sign(b"Foo bar", rng, SPENDING_KEY_GENERATOR); let mut tx = TransactionData::new(); tx.binding_sig = Some(sig); diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index 4edfbc7594..c5442049d8 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -1,31 +1,26 @@ use blake2b_simd::Params; -use crate::{ - consensus, - consensus::NetworkUpgrade, - jubjub::{fs::Fs, JubjubEngine, ToUniform}, - primitives::Rseed, -}; +use crate::{consensus, consensus::NetworkUpgrade, primitives::Rseed}; use ff::Field; use rand_core::{CryptoRng, RngCore}; -pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { +pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr { let mut hasher = Params::new().hash_length(64).personal(persona).to_state(); hasher.update(a); hasher.update(b); let ret = hasher.finalize(); - E::Fs::to_uniform(ret.as_ref()) + jubjub::Fr::from_bytes_wide(ret.as_array()) } pub fn generate_random_rseed( height: u32, rng: &mut R, -) -> Rseed { +) -> Rseed { if P::is_nu_active(NetworkUpgrade::Canopy, height) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) } else { - Rseed::BeforeZip212(Fs::random(rng)) + Rseed::BeforeZip212(jubjub::Fr::random(rng)) } } diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 1271a2d25e..4a7c6d5162 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -6,18 +6,16 @@ use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; -use pairing::bls12_381::Bls12; use std::ops::AddAssign; use crate::{ - jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform}, + constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, primitives::{Diversifier, PaymentAddress, ViewingKey}, }; use std::io::{self, Read, Write}; -use crate::{ - keys::{prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey}, - JUBJUB, +use crate::keys::{ + prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, }; pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling"; @@ -36,8 +34,8 @@ fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingK /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); -impl From<&FullViewingKey> for FVKFingerprint { - fn from(fvk: &FullViewingKey) -> Self { +impl From<&FullViewingKey> for FVKFingerprint { + fn from(fvk: &FullViewingKey) -> Self { let mut h = Blake2bParams::new() .hash_length(32) .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION) @@ -154,7 +152,7 @@ impl DiversifierKey { let d_j = Diversifier(d_j); // Return (j, d_j) if valid, else increment j and try again - match d_j.g_d::(&JUBJUB) { + match d_j.g_d() { Some(_) => return Ok((j, d_j)), None => { if j.increment().is_err() { @@ -173,7 +171,7 @@ pub struct ExtendedSpendingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - pub expsk: ExpandedSpendingKey, + pub expsk: ExpandedSpendingKey, dk: DiversifierKey, } @@ -184,7 +182,7 @@ pub struct ExtendedFullViewingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - pub fvk: FullViewingKey, + pub fvk: FullViewingKey, dk: DiversifierKey, } @@ -297,7 +295,7 @@ impl ExtendedSpendingKey { } pub fn derive_child(&self, i: ChildIndex) -> Self { - let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk, &JUBJUB); + let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); let tmp = match i { ChildIndex::Hardened(i) => { let mut le_i = [0; 4]; @@ -326,8 +324,8 @@ impl ExtendedSpendingKey { child_index: i, chain_code: ChainCode(c_i), expsk: { - let mut ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); - let mut nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + let mut ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array()); + let mut nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array()); ask.add_assign(&self.expsk.ask); nsk.add_assign(&self.expsk.nsk); let ovk = derive_child_ovk(&self.expsk.ovk, i_l); @@ -337,7 +335,7 @@ impl ExtendedSpendingKey { } } - pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { ExtendedFullViewingKey::from(self).default_address() } } @@ -349,7 +347,7 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { parent_fvk_tag: xsk.parent_fvk_tag, child_index: xsk.child_index, chain_code: xsk.chain_code, - fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB), + fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk), dk: xsk.dk, } } @@ -363,7 +361,7 @@ impl ExtendedFullViewingKey { let i = reader.read_u32::()?; let mut c = [0; 32]; reader.read_exact(&mut c)?; - let fvk = FullViewingKey::read(&mut reader, &*JUBJUB)?; + let fvk = FullViewingKey::read(&mut reader)?; let mut dk = [0; 32]; reader.read_exact(&mut dk)?; @@ -410,16 +408,10 @@ impl ExtendedFullViewingKey { child_index: i, chain_code: ChainCode(c_i), fvk: { - let i_ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); - let i_nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); - let ak = JUBJUB - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(i_ask, &JUBJUB) - .add(&self.fvk.vk.ak, &JUBJUB); - let nk = JUBJUB - .generator(FixedGenerators::ProofGenerationKey) - .mul(i_nsk, &JUBJUB) - .add(&self.fvk.vk.nk, &JUBJUB); + let i_ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array()); + let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array()); + let ak = (SPENDING_KEY_GENERATOR * i_ask) + self.fvk.vk.ak; + let nk = (PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk; FullViewingKey { vk: ViewingKey { ak, nk }, @@ -430,21 +422,18 @@ impl ExtendedFullViewingKey { }) } - pub fn address( - &self, - j: DiversifierIndex, - ) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn address(&self, j: DiversifierIndex) -> Result<(DiversifierIndex, PaymentAddress), ()> { let (j, d_j) = match self.dk.diversifier(j) { Ok(ret) => ret, Err(()) => return Err(()), }; - match self.fvk.vk.to_payment_address(d_j, &JUBJUB) { + match self.fvk.vk.to_payment_address(d_j) { Some(addr) => Ok((j, addr)), None => Err(()), } } - pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { self.address(DiversifierIndex::new()) } } @@ -454,6 +443,7 @@ mod tests { use super::*; use ff::PrimeField; + use group::GroupEncoding; #[test] fn derive_nonhardened_child() { @@ -1030,11 +1020,8 @@ mod tests { let xfvk = &xfvks[j]; let tv = &test_vectors[j]; - let mut buf = [0; 32]; - xfvk.fvk.vk.ak.write(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.ak); - xfvk.fvk.vk.nk.write(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.nk); + assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak); + assert_eq!(xfvk.fvk.vk.nk.to_bytes(), tv.nk); assert_eq!(xfvk.fvk.ovk.0, tv.ovk); assert_eq!(xfvk.dk.0, tv.dk); diff --git a/zcash_proofs/examples/bench.rs b/zcash_proofs/examples/bench.rs index cbf6b5e099..776d428313 100644 --- a/zcash_proofs/examples/bench.rs +++ b/zcash_proofs/examples/bench.rs @@ -1,17 +1,16 @@ use bellman::groth16::*; +use bls12_381::Bls12; use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; +use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use std::time::{Duration, Instant}; -use zcash_primitives::jubjub::{edwards, fs, JubjubBls12}; use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment}; use zcash_proofs::circuit::sapling::Spend; const TREE_DEPTH: usize = 32; fn main() { - let jubjub_params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -19,8 +18,7 @@ fn main() { println!("Creating sample parameters..."); let groth_params = generate_random_parameters::( - Spend:: { - params: jubjub_params, + Spend { value_commitment: None, proof_generation_key: None, payment_address: None, @@ -37,20 +35,20 @@ fn main() { let mut total_time = Duration::new(0, 0); for _ in 0..SAMPLES { - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value: 1, - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(jubjub_params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -61,21 +59,21 @@ fn main() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, jubjub_params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH]; - let ar = fs::Fs::random(rng); - let anchor = Fr::random(rng); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH]; + let ar = jubjub::Fr::random(rng); + let anchor = bls12_381::Scalar::random(rng); let start = Instant::now(); let _ = create_random_proof( Spend { - params: jubjub_params, value_commitment: Some(value_commitment), proof_generation_key: Some(proof_generation_key), payment_address: Some(payment_address), diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 1e573da55a..0ca3c0e5e3 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -1,7 +1,5 @@ //! Gadgets implementing Jubjub elliptic curve operations. -use ff::Field; -use pairing::Engine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use bellman::{ConstraintSystem, SynthesisError}; @@ -10,38 +8,34 @@ use bellman::gadgets::Assignment; use bellman::gadgets::num::{AllocatedNum, Num}; -use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams}; - use bellman::gadgets::lookup::lookup3_xy; use bellman::gadgets::boolean::Boolean; +use group::Curve; + +use crate::constants::{FixedGenerator, EDWARDS_D, MONTGOMERY_A, MONTGOMERY_SCALE}; + #[derive(Clone)] -pub struct EdwardsPoint { - x: AllocatedNum, - y: AllocatedNum, +pub struct EdwardsPoint { + x: AllocatedNum, + y: AllocatedNum, } /// Perform a fixed-base scalar multiplication with /// `by` being in little-endian bit order. -pub fn fixed_base_multiplication( +pub fn fixed_base_multiplication( mut cs: CS, - base: FixedGenerators, + base: FixedGenerator, by: &[Boolean], - params: &E::Params, -) -> Result, SynthesisError> +) -> Result where - CS: ConstraintSystem, - E: JubjubEngine, + CS: ConstraintSystem, { // Represents the result of the multiplication let mut result = None; - for (i, (chunk, window)) in by - .chunks(3) - .zip(params.circuit_generators(base).iter()) - .enumerate() - { + for (i, (chunk, window)) in by.chunks(3).zip(base.iter()).enumerate() { let chunk_a = chunk .get(0) .cloned() @@ -66,37 +60,33 @@ where if result.is_none() { result = Some(p); } else { - result = Some(result.unwrap().add( - cs.namespace(|| format!("addition {}", i)), - &p, - params, - )?); + result = Some( + result + .unwrap() + .add(cs.namespace(|| format!("addition {}", i)), &p)?, + ); } } Ok(result.get()?.clone()) } -impl EdwardsPoint { - pub fn get_x(&self) -> &AllocatedNum { +impl EdwardsPoint { + pub fn get_x(&self) -> &AllocatedNum { &self.x } - pub fn get_y(&self) -> &AllocatedNum { + pub fn get_y(&self) -> &AllocatedNum { &self.y } - pub fn assert_not_small_order( - &self, - mut cs: CS, - params: &E::Params, - ) -> Result<(), SynthesisError> + pub fn assert_not_small_order(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let tmp = self.double(cs.namespace(|| "first doubling"), params)?; - let tmp = tmp.double(cs.namespace(|| "second doubling"), params)?; - let tmp = tmp.double(cs.namespace(|| "third doubling"), params)?; + let tmp = self.double(cs.namespace(|| "first doubling"))?; + let tmp = tmp.double(cs.namespace(|| "second doubling"))?; + let tmp = tmp.double(cs.namespace(|| "third doubling"))?; // (0, -1) is a small order point, but won't ever appear here // because cofactor is 2^3, and we performed three doublings. @@ -109,7 +99,7 @@ impl EdwardsPoint { pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { self.x.inputize(cs.namespace(|| "x"))?; self.y.inputize(cs.namespace(|| "y"))?; @@ -120,7 +110,7 @@ impl EdwardsPoint { /// This converts the point into a representation. pub fn repr(&self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut tmp = vec![]; @@ -136,23 +126,19 @@ impl EdwardsPoint { /// This 'witnesses' a point inside the constraint system. /// It guarantees the point is on the curve. - pub fn witness( - mut cs: CS, - p: Option>, - params: &E::Params, - ) -> Result + pub fn witness(mut cs: CS, p: Option) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let p = p.map(|p| p.to_xy()); + let p = p.map(|p| p.to_affine()); // Allocate x - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(p.get()?.0))?; + let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(p.get()?.get_u()))?; // Allocate y - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(p.get()?.1))?; + let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(p.get()?.get_v()))?; - Self::interpret(cs.namespace(|| "point interpretation"), &x, &y, params) + Self::interpret(cs.namespace(|| "point interpretation"), &x, &y) } /// Returns `self` if condition is true, and the neutral @@ -163,14 +149,14 @@ impl EdwardsPoint { condition: &Boolean, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute x' = self.x if condition, and 0 otherwise let x_prime = AllocatedNum::alloc(cs.namespace(|| "x'"), || { if *condition.get_value().get()? { Ok(*self.x.get_value().get()?) } else { - Ok(E::Fr::zero()) + Ok(bls12_381::Scalar::zero()) } })?; @@ -181,7 +167,7 @@ impl EdwardsPoint { cs.enforce( || "x' computation", |lc| lc + self.x.get_variable(), - |_| condition.lc(one, E::Fr::one()), + |_| condition.lc(one, bls12_381::Scalar::one()), |lc| lc + x_prime.get_variable(), ); @@ -190,7 +176,7 @@ impl EdwardsPoint { if *condition.get_value().get()? { Ok(*self.y.get_value().get()?) } else { - Ok(E::Fr::one()) + Ok(bls12_381::Scalar::one()) } })?; @@ -200,8 +186,8 @@ impl EdwardsPoint { cs.enforce( || "y' computation", |lc| lc + self.y.get_variable(), - |_| condition.lc(one, E::Fr::one()), - |lc| lc + y_prime.get_variable() - &condition.not().lc(one, E::Fr::one()), + |_| condition.lc(one, bls12_381::Scalar::one()), + |lc| lc + y_prime.get_variable() - &condition.not().lc(one, bls12_381::Scalar::one()), ); Ok(EdwardsPoint { @@ -213,14 +199,9 @@ impl EdwardsPoint { /// Performs a scalar multiplication of this twisted Edwards /// point by a scalar represented as a sequence of booleans /// in little-endian bit order. - pub fn mul( - &self, - mut cs: CS, - by: &[Boolean], - params: &E::Params, - ) -> Result + pub fn mul(&self, mut cs: CS, by: &[Boolean]) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Represents the current "magnitude" of the base // that we're operating over. Starts at self, @@ -238,7 +219,7 @@ impl EdwardsPoint { curbase = Some( curbase .unwrap() - .double(cs.namespace(|| format!("doubling {}", i)), params)?, + .double(cs.namespace(|| format!("doubling {}", i)))?, ); } @@ -254,11 +235,11 @@ impl EdwardsPoint { if result.is_none() { result = Some(thisbase); } else { - result = Some(result.unwrap().add( - cs.namespace(|| format!("addition {}", i)), - &thisbase, - params, - )?); + result = Some( + result + .unwrap() + .add(cs.namespace(|| format!("addition {}", i)), &thisbase)?, + ); } } @@ -267,12 +248,11 @@ impl EdwardsPoint { pub fn interpret( mut cs: CS, - x: &AllocatedNum, - y: &AllocatedNum, - params: &E::Params, + x: &AllocatedNum, + y: &AllocatedNum, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // -x^2 + y^2 = 1 + dx^2y^2 @@ -285,7 +265,7 @@ impl EdwardsPoint { || "on curve check", |lc| lc - x2.get_variable() + y2.get_variable(), |lc| lc + one, - |lc| lc + one + (*params.edwards_d(), x2y2.get_variable()), + |lc| lc + one + (EDWARDS_D, x2y2.get_variable()), ); Ok(EdwardsPoint { @@ -294,9 +274,9 @@ impl EdwardsPoint { }) } - pub fn double(&self, mut cs: CS, params: &E::Params) -> Result + pub fn double(&self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute T = (x1 + y1) * (x1 + y1) let t = AllocatedNum::alloc(cs.namespace(|| "T"), || { @@ -324,14 +304,14 @@ impl EdwardsPoint { // Compute C = d*A*A let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { let mut t0 = a.get_value().get()?.square(); - t0.mul_assign(params.edwards_d()); + t0.mul_assign(EDWARDS_D); Ok(t0) })?; cs.enforce( || "C computation", - |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + (EDWARDS_D, a.get_variable()), |lc| lc + a.get_variable(), |lc| lc + c.get_variable(), ); @@ -341,7 +321,7 @@ impl EdwardsPoint { let mut t0 = *a.get_value().get()?; t0 = t0.double(); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.add_assign(c.get_value().get()?); let res = t1.invert().map(|t1| t0 * &t1); @@ -366,7 +346,7 @@ impl EdwardsPoint { t0 = t0.double().neg(); t0.add_assign(t.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.sub_assign(c.get_value().get()?); let res = t1.invert().map(|t1| t0 * &t1); @@ -388,14 +368,9 @@ impl EdwardsPoint { } /// Perform addition between any two points - pub fn add( - &self, - mut cs: CS, - other: &Self, - params: &E::Params, - ) -> Result + pub fn add(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute U = (x1 + y1) * (x2 + y2) let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { @@ -427,14 +402,14 @@ impl EdwardsPoint { let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { let mut t0 = *a.get_value().get()?; t0.mul_assign(b.get_value().get()?); - t0.mul_assign(params.edwards_d()); + t0.mul_assign(EDWARDS_D); Ok(t0) })?; cs.enforce( || "C computation", - |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + (EDWARDS_D, a.get_variable()), |lc| lc + b.get_variable(), |lc| lc + c.get_variable(), ); @@ -444,7 +419,7 @@ impl EdwardsPoint { let mut t0 = *a.get_value().get()?; t0.add_assign(b.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.add_assign(c.get_value().get()?); let ret = t1.invert().map(|t1| t0 * &t1); @@ -469,7 +444,7 @@ impl EdwardsPoint { t0.sub_assign(a.get_value().get()?); t0.sub_assign(b.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.sub_assign(c.get_value().get()?); let ret = t1.invert().map(|t1| t0 * &t1); @@ -491,27 +466,23 @@ impl EdwardsPoint { } } -pub struct MontgomeryPoint { - x: Num, - y: Num, +pub struct MontgomeryPoint { + x: Num, + y: Num, } -impl MontgomeryPoint { +impl MontgomeryPoint { /// Converts an element in the prime order subgroup into /// a point in the birationally equivalent twisted /// Edwards curve. - pub fn into_edwards( - self, - mut cs: CS, - params: &E::Params, - ) -> Result, SynthesisError> + pub fn into_edwards(self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute u = (scale*x) / y let u = AllocatedNum::alloc(cs.namespace(|| "u"), || { let mut t0 = *self.x.get_value().get()?; - t0.mul_assign(params.scale()); + t0.mul_assign(MONTGOMERY_SCALE); let ret = self.y.get_value().get()?.invert().map(|invy| t0 * &invy); if bool::from(ret.is_some()) { @@ -523,17 +494,17 @@ impl MontgomeryPoint { cs.enforce( || "u computation", - |lc| lc + &self.y.lc(E::Fr::one()), + |lc| lc + &self.y.lc(bls12_381::Scalar::one()), |lc| lc + u.get_variable(), - |lc| lc + &self.x.lc(*params.scale()), + |lc| lc + &self.x.lc(MONTGOMERY_SCALE), ); // Compute v = (x - 1) / (x + 1) let v = AllocatedNum::alloc(cs.namespace(|| "v"), || { let mut t0 = *self.x.get_value().get()?; let mut t1 = t0; - t0.sub_assign(&E::Fr::one()); - t1.add_assign(&E::Fr::one()); + t0.sub_assign(&bls12_381::Scalar::one()); + t1.add_assign(&bls12_381::Scalar::one()); let ret = t1.invert().map(|t1| t0 * &t1); if bool::from(ret.is_some()) { @@ -546,9 +517,9 @@ impl MontgomeryPoint { let one = CS::one(); cs.enforce( || "v computation", - |lc| lc + &self.x.lc(E::Fr::one()) + one, + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) + one, |lc| lc + v.get_variable(), - |lc| lc + &self.x.lc(E::Fr::one()) - one, + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) - one, ); Ok(EdwardsPoint { x: u, y: v }) @@ -558,20 +529,15 @@ impl MontgomeryPoint { /// in Montgomery, does not check that it's /// on the curve. Useful for constants and /// window table lookups. - pub fn interpret_unchecked(x: Num, y: Num) -> Self { + pub fn interpret_unchecked(x: Num, y: Num) -> Self { MontgomeryPoint { x, y } } /// Performs an affine point addition, not defined for /// coincident points. - pub fn add( - &self, - mut cs: CS, - other: &Self, - params: &E::Params, - ) -> Result + pub fn add(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute lambda = (y' - y) / (x' - x) let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { @@ -591,15 +557,15 @@ impl MontgomeryPoint { cs.enforce( || "evaluate lambda", - |lc| lc + &other.x.lc(E::Fr::one()) - &self.x.lc(E::Fr::one()), + |lc| lc + &other.x.lc(bls12_381::Scalar::one()) - &self.x.lc(bls12_381::Scalar::one()), |lc| lc + lambda.get_variable(), - |lc| lc + &other.y.lc(E::Fr::one()) - &self.y.lc(E::Fr::one()), + |lc| lc + &other.y.lc(bls12_381::Scalar::one()) - &self.y.lc(bls12_381::Scalar::one()), ); // Compute x'' = lambda^2 - A - x - x' let xprime = AllocatedNum::alloc(cs.namespace(|| "xprime"), || { let mut t0 = lambda.get_value().get()?.square(); - t0.sub_assign(params.montgomery_a()); + t0.sub_assign(MONTGOMERY_A); t0.sub_assign(self.x.get_value().get()?); t0.sub_assign(other.x.get_value().get()?); @@ -613,9 +579,9 @@ impl MontgomeryPoint { |lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(), |lc| { - lc + (*params.montgomery_a(), one) - + &self.x.lc(E::Fr::one()) - + &other.x.lc(E::Fr::one()) + lc + (MONTGOMERY_A, one) + + &self.x.lc(bls12_381::Scalar::one()) + + &other.x.lc(bls12_381::Scalar::one()) + xprime.get_variable() }, ); @@ -634,9 +600,9 @@ impl MontgomeryPoint { // y' + y = lambda(x - x') cs.enforce( || "evaluate yprime", - |lc| lc + &self.x.lc(E::Fr::one()) - xprime.get_variable(), + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) - xprime.get_variable(), |lc| lc + lambda.get_variable(), - |lc| lc + yprime.get_variable() + &self.y.lc(E::Fr::one()), + |lc| lc + yprime.get_variable() + &self.y.lc(bls12_381::Scalar::one()), ); Ok(MontgomeryPoint { @@ -650,23 +616,18 @@ impl MontgomeryPoint { mod test { use bellman::ConstraintSystem; use ff::{BitIterator, Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; + use group::{Curve, Group}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; - use std::ops::SubAssign; use bellman::gadgets::test::*; - use zcash_primitives::jubjub::fs::Fs; - use zcash_primitives::jubjub::{ - edwards, montgomery, FixedGenerators, JubjubBls12, JubjubParams, - }; use super::{fixed_base_multiplication, AllocatedNum, EdwardsPoint, MontgomeryPoint}; + use crate::constants::{to_montgomery_coords, NOTE_COMMITMENT_RANDOMNESS_GENERATOR}; use bellman::gadgets::boolean::{AllocatedBit, Boolean}; #[test] fn test_into_edwards() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -675,27 +636,28 @@ mod test { for _ in 0..100 { let mut cs = TestConstraintSystem::new(); - let p = montgomery::Point::::rand(rng, params); - let (u, v) = edwards::Point::from_montgomery(&p, params).to_xy(); - let (x, y) = p.to_xy().unwrap(); + let p = jubjub::ExtendedPoint::random(rng); + let (x, y) = to_montgomery_coords(p).unwrap(); + let p = p.to_affine(); + let (u, v) = (p.get_u(), p.get_v()); let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || Ok(y)).unwrap(); - let p = MontgomeryPoint::::interpret_unchecked(numx.into(), numy.into()); + let p = MontgomeryPoint::interpret_unchecked(numx.into(), numy.into()); - let q = p.into_edwards(&mut cs, params).unwrap(); + let q = p.into_edwards(&mut cs).unwrap(); assert!(cs.is_satisfied()); assert!(q.x.get_value().unwrap() == u); assert!(q.y.get_value().unwrap() == v); - cs.set("u/num", Fr::random(rng)); + cs.set("u/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied().unwrap(), "u computation"); cs.set("u/num", u); assert!(cs.is_satisfied()); - cs.set("v/num", Fr::random(rng)); + cs.set("v/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied().unwrap(), "v computation"); cs.set("v/num", v); assert!(cs.is_satisfied()); @@ -704,34 +666,33 @@ mod test { #[test] fn test_interpret() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p = edwards::Point::::rand(rng, ¶ms); + let p = jubjub::ExtendedPoint::random(rng); let mut cs = TestConstraintSystem::new(); - let q = EdwardsPoint::witness(&mut cs, Some(p.clone()), ¶ms).unwrap(); + let q = EdwardsPoint::witness(&mut cs, Some(p.clone())).unwrap(); - let p = p.to_xy(); + let p = p.to_affine(); assert!(cs.is_satisfied()); - assert_eq!(q.x.get_value().unwrap(), p.0); - assert_eq!(q.y.get_value().unwrap(), p.1); + assert_eq!(q.x.get_value().unwrap(), p.get_u()); + assert_eq!(q.y.get_value().unwrap(), p.get_v()); } for _ in 0..100 { - let p = edwards::Point::::rand(rng, ¶ms); - let (x, y) = p.to_xy(); + let p = jubjub::ExtendedPoint::random(rng).to_affine(); + let (x, y) = (p.get_u(), p.get_v()); let mut cs = TestConstraintSystem::new(); let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); - let p = EdwardsPoint::::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + let p = EdwardsPoint::interpret(&mut cs, &numx, &numy).unwrap(); assert!(cs.is_satisfied()); assert_eq!(p.x.get_value().unwrap(), x); @@ -740,14 +701,14 @@ mod test { // Random (x, y) are unlikely to be on the curve. for _ in 0..100 { - let x = Fr::random(rng); - let y = Fr::random(rng); + let x = bls12_381::Scalar::random(rng); + let y = bls12_381::Scalar::random(rng); let mut cs = TestConstraintSystem::new(); let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); - EdwardsPoint::::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + EdwardsPoint::interpret(&mut cs, &numx, &numy).unwrap(); assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); } @@ -755,23 +716,22 @@ mod test { #[test] fn test_edwards_fixed_base_multiplication() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); - let p = params.generator(FixedGenerators::NoteCommitmentRandomness); - let s = Fs::random(rng); - let q = p.mul(s, params); - let (x1, y1) = q.to_xy(); + let p = zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR; + let s = jubjub::Fr::random(rng); + let q = jubjub::ExtendedPoint::from(p * s).to_affine(); + let (x1, y1) = (q.get_u(), q.get_v()); let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); - s_bits.truncate(Fs::NUM_BITS as usize); + s_bits.truncate(jubjub::Fr::NUM_BITS as usize); let s_bits = s_bits .into_iter() @@ -783,11 +743,10 @@ mod test { .map(|v| Boolean::from(v)) .collect::>(); - let q = fixed_base_multiplication::( + let q = fixed_base_multiplication( cs.namespace(|| "multiplication"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &s_bits, - params, ) .unwrap(); @@ -798,7 +757,6 @@ mod test { #[test] fn test_edwards_multiplication() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -807,24 +765,25 @@ mod test { for _ in 0..100 { let mut cs = TestConstraintSystem::new(); - let p = edwards::Point::::rand(rng, params); - let s = Fs::random(rng); - let q = p.mul(s, params); + let p = jubjub::ExtendedPoint::random(rng); + let s = jubjub::Fr::random(rng); + let q = (p * s).to_affine(); + let p = p.to_affine(); - let (x0, y0) = p.to_xy(); - let (x1, y1) = q.to_xy(); + let (x0, y0) = (p.get_u(), p.get_v()); + let (x1, y1) = (q.get_u(), q.get_v()); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p = EdwardsPoint:: { + let p = EdwardsPoint { x: num_x0, y: num_y0, }; let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); - s_bits.truncate(Fs::NUM_BITS as usize); + s_bits.truncate(jubjub::Fr::NUM_BITS as usize); let s_bits = s_bits .into_iter() @@ -836,9 +795,7 @@ mod test { .map(|v| Boolean::from(v)) .collect::>(); - let q = p - .mul(cs.namespace(|| "scalar mul"), &s_bits, params) - .unwrap(); + let q = p.mul(cs.namespace(|| "scalar mul"), &s_bits).unwrap(); assert!(cs.is_satisfied()); @@ -850,7 +807,6 @@ mod test { #[test] fn test_conditionally_select() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -859,14 +815,14 @@ mod test { for _ in 0..1000 { let mut cs = TestConstraintSystem::new(); - let p = edwards::Point::::rand(rng, params); + let p = jubjub::ExtendedPoint::random(rng).to_affine(); - let (x0, y0) = p.to_xy(); + let (x0, y0) = (p.get_u(), p.get_v()); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p = EdwardsPoint:: { + let p = EdwardsPoint { x: num_x0, y: num_y0, }; @@ -899,13 +855,13 @@ mod test { assert_eq!(q.x.get_value().unwrap(), x0); assert_eq!(q.y.get_value().unwrap(), y0); - cs.set("select/y'/num", Fr::one()); + cs.set("select/y'/num", bls12_381::Scalar::one()); assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); - cs.set("select/x'/num", Fr::zero()); + cs.set("select/x'/num", bls12_381::Scalar::zero()); assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); } else { - assert_eq!(q.x.get_value().unwrap(), Fr::zero()); - assert_eq!(q.y.get_value().unwrap(), Fr::one()); + assert_eq!(q.x.get_value().unwrap(), bls12_381::Scalar::zero()); + assert_eq!(q.y.get_value().unwrap(), bls12_381::Scalar::one()); cs.set("select/y'/num", x0); assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); @@ -917,21 +873,24 @@ mod test { #[test] fn test_edwards_addition() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = edwards::Point::::rand(rng, params); - let p2 = edwards::Point::::rand(rng, params); + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = jubjub::ExtendedPoint::random(rng); - let p3 = p1.add(&p2, params); + let p3 = p1 + p2; - let (x0, y0) = p1.to_xy(); - let (x1, y1) = p2.to_xy(); - let (x2, y2) = p3.to_xy(); + let p1 = p1.to_affine(); + let p2 = p2.to_affine(); + let p3 = p3.to_affine(); + + let (x0, y0) = (p1.get_u(), p1.get_v()); + let (x1, y1) = (p2.get_u(), p2.get_v()); + let (x2, y2) = (p3.get_u(), p3.get_v()); let mut cs = TestConstraintSystem::new(); @@ -941,7 +900,7 @@ mod test { let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); - let p1 = EdwardsPoint:: { + let p1 = EdwardsPoint { x: num_x0, y: num_y0, }; @@ -951,7 +910,7 @@ mod test { y: num_y1, }; - let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + let p3 = p1.add(cs.namespace(|| "addition"), &p2).unwrap(); assert!(cs.is_satisfied()); @@ -959,19 +918,19 @@ mod test { assert!(p3.y.get_value().unwrap() == y2); let u = cs.get("addition/U/num"); - cs.set("addition/U/num", Fr::random(rng)); + cs.set("addition/U/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/U computation")); cs.set("addition/U/num", u); assert!(cs.is_satisfied()); let x3 = cs.get("addition/x3/num"); - cs.set("addition/x3/num", Fr::random(rng)); + cs.set("addition/x3/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/x3 computation")); cs.set("addition/x3/num", x3); assert!(cs.is_satisfied()); let y3 = cs.get("addition/y3/num"); - cs.set("addition/y3/num", Fr::random(rng)); + cs.set("addition/y3/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/y3 computation")); cs.set("addition/y3/num", y3); assert!(cs.is_satisfied()); @@ -980,30 +939,32 @@ mod test { #[test] fn test_edwards_doubling() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = edwards::Point::::rand(rng, params); - let p2 = p1.double(params); + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = p1.double(); + + let p1 = p1.to_affine(); + let p2 = p2.to_affine(); - let (x0, y0) = p1.to_xy(); - let (x1, y1) = p2.to_xy(); + let (x0, y0) = (p1.get_u(), p1.get_v()); + let (x1, y1) = (p2.get_u(), p2.get_v()); let mut cs = TestConstraintSystem::new(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); - let p1 = EdwardsPoint:: { + let p1 = EdwardsPoint { x: num_x0, y: num_y0, }; - let p2 = p1.double(cs.namespace(|| "doubling"), params).unwrap(); + let p2 = p1.double(cs.namespace(|| "doubling")).unwrap(); assert!(cs.is_satisfied()); @@ -1014,38 +975,19 @@ mod test { #[test] fn test_montgomery_addition() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = loop { - let x = Fr::random(rng); - let s: bool = rng.next_u32() % 2 != 0; - - let p = montgomery::Point::::get_for_x(x, s, params); - if p.is_some().into() { - break p.unwrap(); - } - }; - - let p2 = loop { - let x = Fr::random(rng); - let s: bool = rng.next_u32() % 2 != 0; - - let p = montgomery::Point::::get_for_x(x, s, params); - if p.is_some().into() { - break p.unwrap(); - } - }; + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = jubjub::ExtendedPoint::random(rng); + let p3 = p1 + p2; - let p3 = p1.add(&p2, params); - - let (x0, y0) = p1.to_xy().unwrap(); - let (x1, y1) = p2.to_xy().unwrap(); - let (x2, y2) = p3.to_xy().unwrap(); + let (x0, y0) = to_montgomery_coords(p1).unwrap(); + let (x1, y1) = to_montgomery_coords(p2).unwrap(); + let (x2, y2) = to_montgomery_coords(p3).unwrap(); let mut cs = TestConstraintSystem::new(); @@ -1055,7 +997,7 @@ mod test { let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); - let p1 = MontgomeryPoint:: { + let p1 = MontgomeryPoint { x: num_x0.into(), y: num_y0.into(), }; @@ -1065,105 +1007,102 @@ mod test { y: num_y1.into(), }; - let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + let p3 = p1.add(cs.namespace(|| "addition"), &p2).unwrap(); assert!(cs.is_satisfied()); assert!(p3.x.get_value().unwrap() == x2); assert!(p3.y.get_value().unwrap() == y2); - cs.set("addition/yprime/num", Fr::random(rng)); + cs.set("addition/yprime/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate yprime")); cs.set("addition/yprime/num", y2); assert!(cs.is_satisfied()); - cs.set("addition/xprime/num", Fr::random(rng)); + cs.set("addition/xprime/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate xprime")); cs.set("addition/xprime/num", x2); assert!(cs.is_satisfied()); - cs.set("addition/lambda/num", Fr::random(rng)); + cs.set("addition/lambda/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate lambda")); } } #[test] fn test_assert_not_small_order() { - let params = &JubjubBls12::new(); - - let check_small_order_from_p = |p: edwards::Point, is_small_order| { + let check_small_order_from_p = |p: jubjub::ExtendedPoint, is_small_order| { let mut cs = TestConstraintSystem::new(); - let p = EdwardsPoint::witness(&mut cs, Some(p), params).unwrap(); + let p = EdwardsPoint::witness(&mut cs, Some(p)).unwrap(); assert!(cs.is_satisfied()); - assert!(p.assert_not_small_order(&mut cs, params).is_err() == is_small_order); + assert!(p.assert_not_small_order(&mut cs).is_err() == is_small_order); }; let check_small_order_from_strs = |x, y| { - //let (x,y) = (Fr::from_str("14080418777298869350588389379361252092475090129841789940098060767181937064268").unwrap(), Fr::from_str("4408371274642418797323679050836535851651768103477128764103246588657558662748").unwrap()); - let (x, y) = (Fr::from_str(x).unwrap(), Fr::from_str(y).unwrap()); - let p = edwards::Point::::get_for_y(y, false, params).unwrap(); - assert_eq!(x, p.to_xy().0); - - check_small_order_from_p(p, true); + //let (x,y) = (bls12_381::Scalar::from_str("14080418777298869350588389379361252092475090129841789940098060767181937064268").unwrap(), bls12_381::Scalar::from_str("4408371274642418797323679050836535851651768103477128764103246588657558662748").unwrap()); + let (x, y) = ( + bls12_381::Scalar::from_str(x).unwrap(), + bls12_381::Scalar::from_str(y).unwrap(), + ); + let p = jubjub::AffinePoint::from_raw_unchecked(x, y); + + check_small_order_from_p(p.into(), true); }; // zero has low order check_small_order_from_strs("0", "1"); // prime subgroup order - let prime_subgroup_order = Fs::from_str( + let prime_subgroup_order = jubjub::Fr::from_str( "6554484396890773809930967563523245729705921265872317281365359162392183254199", ) .unwrap(); - let largest_small_subgroup_order = Fs::from_str("8").unwrap(); + let largest_small_subgroup_order = jubjub::Fr::from_str("8").unwrap(); - let (zero_x, zero_y) = (Fr::from_str("0").unwrap(), Fr::from_str("1").unwrap()); + let (zero_x, zero_y) = (bls12_381::Scalar::zero(), bls12_381::Scalar::one()); // generator for jubjub let (x, y) = ( - Fr::from_str( + bls12_381::Scalar::from_str( "11076627216317271660298050606127911965867021807910416450833192264015104452986", ) .unwrap(), - Fr::from_str( + bls12_381::Scalar::from_str( "44412834903739585386157632289020980010620626017712148233229312325549216099227", ) .unwrap(), ); - let g = edwards::Point::::get_for_y(y, false, params).unwrap(); - assert_eq!(x, g.to_xy().0); - check_small_order_from_p(g.clone(), false); + let g = jubjub::AffinePoint::from_raw_unchecked(x, y).into(); + check_small_order_from_p(g, false); // generator for the prime subgroup - let g_prime = g.mul(largest_small_subgroup_order, params); + let g_prime = g * largest_small_subgroup_order; check_small_order_from_p(g_prime.clone(), false); - let mut prime_subgroup_order_minus_1 = prime_subgroup_order.clone(); - prime_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + let prime_subgroup_order_minus_1 = prime_subgroup_order - jubjub::Fr::one(); - let should_not_be_zero = g_prime.mul(prime_subgroup_order_minus_1, params); - assert_ne!(zero_x, should_not_be_zero.to_xy().0); - assert_ne!(zero_y, should_not_be_zero.to_xy().1); - let should_be_zero = should_not_be_zero.add(&g_prime, params); - assert_eq!(zero_x, should_be_zero.to_xy().0); - assert_eq!(zero_y, should_be_zero.to_xy().1); + let should_not_be_zero = g_prime * prime_subgroup_order_minus_1; + assert_ne!(zero_x, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_y, should_not_be_zero.to_affine().get_v()); + let should_be_zero = should_not_be_zero + g_prime; + assert_eq!(zero_x, should_be_zero.to_affine().get_u()); + assert_eq!(zero_y, should_be_zero.to_affine().get_v()); // generator for the small order subgroup - let g_small = g.mul(prime_subgroup_order_minus_1, params); - let g_small = g_small.add(&g, params); + let g_small = g * prime_subgroup_order_minus_1; + let g_small = g_small + g; check_small_order_from_p(g_small.clone(), true); // g_small does have order 8 - let mut largest_small_subgroup_order_minus_1 = largest_small_subgroup_order.clone(); - largest_small_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + let largest_small_subgroup_order_minus_1 = largest_small_subgroup_order - jubjub::Fr::one(); - let should_not_be_zero = g_small.mul(largest_small_subgroup_order_minus_1, params); - assert_ne!(zero_x, should_not_be_zero.to_xy().0); - assert_ne!(zero_y, should_not_be_zero.to_xy().1); + let should_not_be_zero = g_small * largest_small_subgroup_order_minus_1; + assert_ne!(zero_x, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_y, should_not_be_zero.to_affine().get_v()); - let should_be_zero = should_not_be_zero.add(&g_small, params); - assert_eq!(zero_x, should_be_zero.to_xy().0); - assert_eq!(zero_y, should_be_zero.to_xy().1); + let should_be_zero = should_not_be_zero + g_small; + assert_eq!(zero_x, should_be_zero.to_affine().get_u()); + assert_eq!(zero_y, should_be_zero.to_affine().get_v()); // take all the points from the script // assert should be different than multiplying by cofactor, which is the solution diff --git a/zcash_proofs/src/circuit/pedersen_hash.rs b/zcash_proofs/src/circuit/pedersen_hash.rs index 43278a75c8..06e912c650 100644 --- a/zcash_proofs/src/circuit/pedersen_hash.rs +++ b/zcash_proofs/src/circuit/pedersen_hash.rs @@ -4,9 +4,10 @@ use super::ecc::{EdwardsPoint, MontgomeryPoint}; use bellman::gadgets::boolean::Boolean; use bellman::gadgets::lookup::*; use bellman::{ConstraintSystem, SynthesisError}; -use zcash_primitives::jubjub::*; pub use zcash_primitives::pedersen_hash::Personalization; +use crate::constants::PEDERSEN_CIRCUIT_GENERATORS; + fn get_constant_bools(person: &Personalization) -> Vec { person .get_bits() @@ -15,21 +16,20 @@ fn get_constant_bools(person: &Personalization) -> Vec { .collect() } -pub fn pedersen_hash( +pub fn pedersen_hash( mut cs: CS, personalization: Personalization, bits: &[Boolean], - params: &E::Params, -) -> Result, SynthesisError> +) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let personalization = get_constant_bools(&personalization); assert_eq!(personalization.len(), 6); let mut edwards_result = None; let mut bits = personalization.iter().chain(bits.iter()).peekable(); - let mut segment_generators = params.pedersen_circuit_generators().iter(); + let mut segment_generators = PEDERSEN_CIRCUIT_GENERATORS.iter(); let boolean_false = Boolean::constant(false); let mut segment_i = 0; @@ -60,7 +60,6 @@ where format!("addition of segment {}, window {}", segment_i, window_i) }), segment_result, - params, )?; } } @@ -83,7 +82,6 @@ where // Convert this segment into twisted Edwards form. let segment_result = segment_result.into_edwards( cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)), - params, )?; match edwards_result { @@ -91,7 +89,6 @@ where *edwards_result = segment_result.add( cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)), edwards_result, - params, )?; } None => { @@ -111,7 +108,7 @@ mod test { use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::test::*; use ff::PrimeField; - use pairing::bls12_381::{Bls12, Fr}; + use group::Curve; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::pedersen_hash; @@ -147,7 +144,6 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); let leaves_len = 2 * 255; let note_len = 64 + 256 + 256; @@ -177,11 +173,10 @@ mod test { }) .collect(); - pedersen_hash::( + pedersen_hash( cs.namespace(|| "pedersen hash"), Personalization::NoteCommitment, &input_bools, - params, ) .unwrap(); @@ -206,7 +201,6 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); for length in 0..751 { for _ in 0..5 { @@ -225,36 +219,33 @@ mod test { }) .collect(); - let res = pedersen_hash::( + let res = pedersen_hash( cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, - params, ) .unwrap(); assert!(cs.is_satisfied()); - let expected = pedersen_hash::pedersen_hash::( + let expected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( Personalization::MerkleTree(1), input.clone().into_iter(), - params, - ) - .to_xy(); + )) + .to_affine(); - assert_eq!(res.get_x().get_value().unwrap(), expected.0); - assert_eq!(res.get_y().get_value().unwrap(), expected.1); + assert_eq!(res.get_x().get_value().unwrap(), expected.get_u()); + assert_eq!(res.get_y().get_value().unwrap(), expected.get_v()); // Test against the output of a different personalization - let unexpected = pedersen_hash::pedersen_hash::( + let unexpected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( Personalization::MerkleTree(0), input.into_iter(), - params, - ) - .to_xy(); + )) + .to_affine(); - assert!(res.get_x().get_value().unwrap() != unexpected.0); - assert!(res.get_y().get_value().unwrap() != unexpected.1); + assert!(res.get_x().get_value().unwrap() != unexpected.get_u()); + assert!(res.get_y().get_value().unwrap() != unexpected.get_v()); } } } @@ -265,7 +256,6 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); let expected_xs = [ "28161926966428986673895580777285905189725480206811328272001879986576840909576", @@ -291,11 +281,10 @@ mod test { }) .collect(); - let res = pedersen_hash::( + let res = pedersen_hash( cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, - params, ) .unwrap(); @@ -303,11 +292,11 @@ mod test { assert_eq!( res.get_x().get_value().unwrap(), - Fr::from_str(expected_xs[length - 300]).unwrap() + bls12_381::Scalar::from_str(expected_xs[length - 300]).unwrap() ); assert_eq!( res.get_y().get_value().unwrap(), - Fr::from_str(expected_ys[length - 300]).unwrap() + bls12_381::Scalar::from_str(expected_ys[length - 300]).unwrap() ); } } diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index c486a8e5f7..3e0eec78ff 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -1,17 +1,21 @@ //! The Sapling circuits. -use ff::{Field, PrimeField}; +use ff::PrimeField; +use group::Curve; use bellman::{Circuit, ConstraintSystem, SynthesisError}; -use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; - use zcash_primitives::constants; use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment}; use super::ecc; use super::pedersen_hash; +use crate::constants::{ + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR, + PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + VALUE_COMMITMENT_VALUE_GENERATOR, +}; use bellman::gadgets::blake2s; use bellman::gadgets::boolean; use bellman::gadgets::multipack; @@ -21,60 +25,54 @@ use bellman::gadgets::Assignment; pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH; /// This is an instance of the `Spend` circuit. -pub struct Spend<'a, E: JubjubEngine> { - pub params: &'a E::Params, - +pub struct Spend { /// Pedersen commitment to the value being spent - pub value_commitment: Option>, + pub value_commitment: Option, /// Key required to construct proofs for spending notes /// for a particular spending key - pub proof_generation_key: Option>, + pub proof_generation_key: Option, /// The payment address associated with the note - pub payment_address: Option>, + pub payment_address: Option, /// The randomness of the note commitment - pub commitment_randomness: Option, + pub commitment_randomness: Option, /// Re-randomization of the public key - pub ar: Option, + pub ar: Option, /// The authentication path of the commitment in the tree - pub auth_path: Vec>, + pub auth_path: Vec>, /// The anchor; the root of the tree. If the note being /// spent is zero-value, this can be anything. - pub anchor: Option, + pub anchor: Option, } /// This is an output circuit instance. -pub struct Output<'a, E: JubjubEngine> { - pub params: &'a E::Params, - +pub struct Output { /// Pedersen commitment to the value being spent - pub value_commitment: Option>, + pub value_commitment: Option, /// The payment address of the recipient - pub payment_address: Option>, + pub payment_address: Option, /// The randomness used to hide the note commitment data - pub commitment_randomness: Option, + pub commitment_randomness: Option, /// The ephemeral secret key for DH with recipient - pub esk: Option, + pub esk: Option, } /// Exposes a Pedersen commitment to the value as an /// input to the circuit -fn expose_value_commitment( +fn expose_value_commitment( mut cs: CS, - value_commitment: Option>, - params: &E::Params, + value_commitment: Option, ) -> Result, SynthesisError> where - E: JubjubEngine, - CS: ConstraintSystem, + CS: ConstraintSystem, { // Booleanize the value into little-endian bit order let value_bits = boolean::u64_into_boolean_vec_le( @@ -83,11 +81,10 @@ where )?; // Compute the note value in the exponent - let value = ecc::fixed_base_multiplication::( + let value = ecc::fixed_base_multiplication( cs.namespace(|| "compute the value in the exponent"), - FixedGenerators::ValueCommitmentValue, + &VALUE_COMMITMENT_VALUE_GENERATOR, &value_bits, - params, )?; // Booleanize the randomness. This does not ensure @@ -99,15 +96,14 @@ where )?; // Compute the randomness in the exponent - let rcv = ecc::fixed_base_multiplication::( + let rcv = ecc::fixed_base_multiplication( cs.namespace(|| "computation of rcv"), - FixedGenerators::ValueCommitmentRandomness, + &VALUE_COMMITMENT_RANDOMNESS_GENERATOR, &rcv, - params, )?; // Compute the Pedersen commitment to the value - let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?; + let cv = value.add(cs.namespace(|| "computation of cv"), &rcv)?; // Expose the commitment as an input to the circuit cv.inputize(cs.namespace(|| "commitment point"))?; @@ -115,33 +111,34 @@ where Ok(value_bits) } -impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for Spend { + fn synthesize>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), - self.proof_generation_key.as_ref().map(|k| k.ak.clone()), - self.params, + self.proof_generation_key.as_ref().map(|k| k.ak.into()), )?; // There are no sensible attacks on small order points // of ak (that we're aware of!) but it's a cheap check, // so we do it. - ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?; + ak.assert_not_small_order(cs.namespace(|| "ak not small order"))?; // Rerandomize ak and expose it as an input to the circuit { let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?; // Compute the randomness in the exponent - let ar = ecc::fixed_base_multiplication::( + let ar = ecc::fixed_base_multiplication( cs.namespace(|| "computation of randomization for the signing key"), - FixedGenerators::SpendingKeyGenerator, + &SPENDING_KEY_GENERATOR, &ar, - self.params, )?; - let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?; + let rk = ak.add(cs.namespace(|| "computation of rk"), &ar)?; rk.inputize(cs.namespace(|| "rk"))?; } @@ -161,11 +158,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // congruency then that's equivalent. // Compute nk = [nsk] ProvingPublicKey - nk = ecc::fixed_base_multiplication::( + nk = ecc::fixed_base_multiplication( cs.namespace(|| "computation of nk"), - FixedGenerators::ProofGenerationKey, + &PROOF_GENERATION_KEY_GENERATOR, &nsk, - self.params, )?; } @@ -198,21 +194,15 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // drop_5 to ensure it's in the field - ivk.truncate(E::Fs::CAPACITY as usize); + ivk.truncate(jubjub::Fr::CAPACITY as usize); // Witness g_d, checking that it's on the curve. let g_d = { - // This binding is to avoid a weird edge case in Rust's - // ownership/borrowing rules. self is partially moved - // above, but the closure for and_then will have to - // move self (or a reference to self) to reference - // self.params, so we have to copy self.params here. - let params = self.params; - ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), - self.payment_address.as_ref().and_then(|a| a.g_d(params)), - self.params, + self.payment_address + .as_ref() + .and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)), )? }; @@ -220,10 +210,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // is already done in the Output circuit, and this proof ensures // g_d is bound to a product of that check, but for defense in // depth let's check it anyway. It's cheap. - g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; + g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?; // Compute pk_d = g_d^ivk - let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?; + let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk)?; // Compute note contents: // value (in big endian) followed by g_d and pk_d @@ -237,12 +227,11 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let value_bits = expose_value_commitment( cs.namespace(|| "value commitment"), self.value_commitment, - self.params, )?; // Compute the note's value as a linear combination // of the bits. - let mut coeff = E::Fr::one(); + let mut coeff = bls12_381::Scalar::one(); for bit in &value_bits { value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff); coeff = coeff.double(); @@ -266,11 +255,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { ); // Compute the hash of the note contents - let mut cm = pedersen_hash::pedersen_hash::( + let mut cm = pedersen_hash::pedersen_hash( cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, - self.params, )?; { @@ -281,20 +269,15 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // Compute the note commitment randomness in the exponent - let rcm = ecc::fixed_base_multiplication::( + let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &rcm, - self.params, )?; // Randomize the note commitment. Pedersen hashes are not // themselves hiding commitments. - cm = cm.add( - cs.namespace(|| "randomization of note commitment"), - &rcm, - self.params, - )?; + cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?; } // This will store (least significant bit first) @@ -342,11 +325,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?); // Compute the new subtree value - cur = pedersen_hash::pedersen_hash::( + cur = pedersen_hash::pedersen_hash( cs.namespace(|| "computation of pedersen hash"), pedersen_hash::Personalization::MerkleTree(i), &preimage, - self.params, )? .get_x() .clone(); // Injective encoding @@ -366,7 +348,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { cs.enforce( || "conditionally enforce correct root", |lc| lc + cur.get_variable() - rt.get_variable(), - |lc| lc + &value_num.lc(E::Fr::one()), + |lc| lc + &value_num.lc(bls12_381::Scalar::one()), |lc| lc, ); @@ -379,19 +361,14 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut rho = cm; { // Compute the position in the exponent - let position = ecc::fixed_base_multiplication::( + let position = ecc::fixed_base_multiplication( cs.namespace(|| "g^position"), - FixedGenerators::NullifierPosition, + &NULLIFIER_POSITION_GENERATOR, &position_bits, - self.params, )?; // Add the position to the commitment - rho = rho.add( - cs.namespace(|| "faerie gold prevention"), - &position, - self.params, - )?; + rho = rho.add(cs.namespace(|| "faerie gold prevention"), &position)?; } // Let's compute nf = BLAKE2s(nk || rho) @@ -410,8 +387,11 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { } } -impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for Output { + fn synthesize>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { // Let's start to construct our note, which contains // value (big endian) let mut note_contents = vec![]; @@ -421,19 +401,17 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { note_contents.extend(expose_value_commitment( cs.namespace(|| "value commitment"), self.value_commitment, - self.params, )?); // Let's deal with g_d { - let params = self.params; - // Prover witnesses g_d, ensuring it's on the // curve. let g_d = ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), - self.payment_address.as_ref().and_then(|a| a.g_d(params)), - self.params, + self.payment_address + .as_ref() + .and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)), )?; // g_d is ensured to be large order. The relationship @@ -445,7 +423,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // // Further, if it were small order, epk would be // small order too! - g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; + g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?; // Extend our note contents with the representation of // g_d. @@ -455,7 +433,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?; // Create the ephemeral public key from g_d. - let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?; + let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk)?; // Expose epk publicly. epk.inputize(cs.namespace(|| "epk"))?; @@ -466,19 +444,22 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // they would like. { // Just grab pk_d from the witness - let pk_d = self.payment_address.as_ref().map(|e| e.pk_d().to_xy()); + let pk_d = self + .payment_address + .as_ref() + .map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine()); // Witness the y-coordinate, encoded as little // endian bits (to match the representation) let y_contents = boolean::field_into_boolean_vec_le( cs.namespace(|| "pk_d bits of y"), - pk_d.map(|e| e.1), + pk_d.map(|e| e.get_v()), )?; // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( cs.namespace(|| "pk_d bit of x"), - pk_d.map(|e| e.0.is_odd()), + pk_d.map(|e| e.get_u().is_odd()), )?); // Extend the note with pk_d representation @@ -494,11 +475,10 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { ); // Compute the hash of the note contents - let mut cm = pedersen_hash::pedersen_hash::( + let mut cm = pedersen_hash::pedersen_hash( cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, - self.params, )?; { @@ -509,19 +489,14 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { )?; // Compute the note commitment randomness in the exponent - let rcm = ecc::fixed_base_multiplication::( + let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &rcm, - self.params, )?; // Randomize our note commitment - cm = cm.add( - cs.namespace(|| "randomization of note commitment"), - &rcm, - self.params, - )?; + cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?; } // Only the x-coordinate of the output is revealed, @@ -538,16 +513,14 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { fn test_input_circuit_with_bls12_381() { use bellman::gadgets::test::*; use ff::{BitIterator, Field}; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -556,20 +529,20 @@ fn test_input_circuit_with_bls12_381() { let tree_depth = 32; for _ in 0..10 { - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value: rng.next_u64(), - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -580,20 +553,21 @@ fn test_input_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let g_d = payment_address.diversifier().g_d(params).unwrap(); - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; - let ar = fs::Fs::random(rng); + let g_d = payment_address.diversifier().g_d().unwrap(); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; + let ar = jubjub::Fr::random(rng); { - let rk = viewing_key.rk(ar, params).to_xy(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); + let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); let note = Note { value: value_commitment.value, g_d: g_d.clone(), @@ -602,7 +576,7 @@ fn test_input_circuit_with_bls12_381() { }; let mut position = 0u64; - let cm: Fr = note.cm(params); + let cm = note.cm(); let mut cur = cm.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { @@ -621,22 +595,21 @@ fn test_input_circuit_with_bls12_381() { lhs.reverse(); rhs.reverse(); - cur = pedersen_hash::pedersen_hash::( + cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), lhs.into_iter() - .take(Fr::NUM_BITS as usize) - .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), - params, - ) - .to_xy() - .0; + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)), + )) + .to_affine() + .get_u(); if b { position |= 1 << i; } } - let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = note.nf(&viewing_key, position); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); @@ -644,7 +617,6 @@ fn test_input_circuit_with_bls12_381() { let mut cs = TestConstraintSystem::new(); let instance = Spend { - params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key.clone()), payment_address: Some(payment_address.clone()), @@ -666,16 +638,16 @@ fn test_input_circuit_with_bls12_381() { assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); assert_eq!(cs.num_inputs(), 8); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); assert_eq!( cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + expected_value_cm.get_u() ); assert_eq!( cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + expected_value_cm.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -688,16 +660,14 @@ fn test_input_circuit_with_bls12_381() { fn test_input_circuit_with_bls12_381_external_test_vectors() { use bellman::gadgets::test::*; use ff::{BitIterator, Field}; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -732,20 +702,20 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ]; for i in 0..10 { - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value: i, - randomness: fs::Fs::from_str(&(1000 * (i + 1)).to_string()).unwrap(), + randomness: jubjub::Fr::from_str(&(1000 * (i + 1)).to_string()).unwrap(), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -756,27 +726,28 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let g_d = payment_address.diversifier().g_d(params).unwrap(); - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; - let ar = fs::Fs::random(rng); + let g_d = payment_address.diversifier().g_d().unwrap(); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; + let ar = jubjub::Fr::random(rng); { - let rk = viewing_key.rk(ar, params).to_xy(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); + let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); assert_eq!( - expected_value_cm.0, - Fr::from_str(&expected_cm_xs[i as usize]).unwrap() + expected_value_cm.get_u(), + bls12_381::Scalar::from_str(&expected_cm_xs[i as usize]).unwrap() ); assert_eq!( - expected_value_cm.1, - Fr::from_str(&expected_cm_ys[i as usize]).unwrap() + expected_value_cm.get_v(), + bls12_381::Scalar::from_str(&expected_cm_ys[i as usize]).unwrap() ); let note = Note { value: value_commitment.value, @@ -786,7 +757,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { }; let mut position = 0u64; - let cm: Fr = note.cm(params); + let cm = note.cm(); let mut cur = cm.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { @@ -805,22 +776,21 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { lhs.reverse(); rhs.reverse(); - cur = pedersen_hash::pedersen_hash::( + cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), lhs.into_iter() - .take(Fr::NUM_BITS as usize) - .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), - params, - ) - .to_xy() - .0; + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)), + )) + .to_affine() + .get_u(); if b { position |= 1 << i; } } - let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = note.nf(&viewing_key, position); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); @@ -828,7 +798,6 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let mut cs = TestConstraintSystem::new(); let instance = Spend { - params: params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key.clone()), payment_address: Some(payment_address.clone()), @@ -850,16 +819,16 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); assert_eq!(cs.num_inputs(), 8); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); assert_eq!( cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + expected_value_cm.get_u() ); assert_eq!( cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + expected_value_cm.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -872,35 +841,31 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { fn test_output_circuit_with_bls12_381() { use bellman::gadgets::test::*; use ff::Field; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; - use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, - primitives::{Diversifier, ProofGenerationKey, Rseed}, - }; + use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, Rseed}; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value: rng.next_u64(), - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -911,20 +876,19 @@ fn test_output_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let commitment_randomness = fs::Fs::random(rng); - let esk = fs::Fs::random(rng); + let commitment_randomness = jubjub::Fr::random(rng); + let esk = jubjub::Fr::random(rng); { let mut cs = TestConstraintSystem::new(); let instance = Output { - params, value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), @@ -944,31 +908,34 @@ fn test_output_circuit_with_bls12_381() { .create_note( value_commitment.value, Rseed::BeforeZip212(commitment_randomness), - params, ) .expect("should be valid") - .cm(params); + .cm(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); - let expected_epk = payment_address - .g_d(params) - .expect("should be valid") - .mul(esk, params); - let expected_epk_xy = expected_epk.to_xy(); + let expected_epk = + jubjub::ExtendedPoint::from(payment_address.g_d().expect("should be valid") * esk) + .to_affine(); assert_eq!(cs.num_inputs(), 6); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); assert_eq!( cs.get_input(1, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + expected_value_cm.get_u() ); assert_eq!( cs.get_input(2, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + expected_value_cm.get_v() + ); + assert_eq!( + cs.get_input(3, "epk/x/input variable"), + expected_epk.get_u() + ); + assert_eq!( + cs.get_input(4, "epk/y/input variable"), + expected_epk.get_v() ); - assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); - assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); } } diff --git a/zcash_proofs/src/circuit/sprout/mod.rs b/zcash_proofs/src/circuit/sprout/mod.rs index b2653ab24d..ddb55deb92 100644 --- a/zcash_proofs/src/circuit/sprout/mod.rs +++ b/zcash_proofs/src/circuit/sprout/mod.rs @@ -337,7 +337,7 @@ where #[ignore] fn test_sprout_constraints() { use bellman::gadgets::test::*; - use pairing::bls12_381::Fr; + use bls12_381::Scalar; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -355,7 +355,7 @@ fn test_sprout_constraints() { } while test_vector.len() != 0 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let phi = Some(get_u256(&mut test_vector)); let rt = Some(get_u256(&mut test_vector)); diff --git a/zcash_proofs/src/constants.rs b/zcash_proofs/src/constants.rs index f02d8e5d0c..769e2ed62e 100644 --- a/zcash_proofs/src/constants.rs +++ b/zcash_proofs/src/constants.rs @@ -168,9 +168,6 @@ fn generate_pedersen_circuit_generators() -> Vec>> { #[cfg(test)] mod tests { - use bls12_381::Scalar; - use ff::PrimeField; - use pairing::bls12_381::Fr; use zcash_primitives::{ jubjub::{FixedGenerators, JubjubParams}, JUBJUB, @@ -178,10 +175,6 @@ mod tests { use super::*; - fn check_scalar(expected: Fr, actual: Scalar) { - assert_eq!(expected.to_repr().0, actual.to_bytes()); - } - fn check_generator(expected: FixedGenerators, actual: FixedGenerator) { let expected = JUBJUB.circuit_generators(expected); @@ -192,25 +185,25 @@ mod tests { assert_eq!(expected.len(), actual.len()); for (expected, actual) in expected.iter().zip(actual) { // Same coordinates. - check_scalar(expected.0, actual.0); - check_scalar(expected.1, actual.1); + assert_eq!(expected.0, actual.0); + assert_eq!(expected.1, actual.1); } } } #[test] fn edwards_d() { - check_scalar(*JUBJUB.edwards_d(), EDWARDS_D); + assert_eq!(*JUBJUB.edwards_d(), EDWARDS_D); } #[test] fn montgomery_a() { - check_scalar(*JUBJUB.montgomery_a(), MONTGOMERY_A); + assert_eq!(*JUBJUB.montgomery_a(), MONTGOMERY_A); } #[test] fn montgomery_scale() { - check_scalar(*JUBJUB.scale(), MONTGOMERY_SCALE); + assert_eq!(*JUBJUB.scale(), MONTGOMERY_SCALE); } #[test] @@ -268,26 +261,4 @@ mod tests { &SPENDING_KEY_GENERATOR, ); } - - #[test] - fn pedersen_circuit_generators() { - let expected = JUBJUB.pedersen_circuit_generators(); - let actual = &PEDERSEN_CIRCUIT_GENERATORS; - - // Same number of Pedersen hash generators. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual.iter()) { - // Same number of windows per generator. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same size table per window. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same coordinates. - check_scalar(expected.0, actual.0); - check_scalar(expected.1, actual.1); - } - } - } - } } diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index a94c4a5897..ecbb34c0bf 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -7,7 +7,7 @@ #![deny(intra_doc_link_resolution_failure)] use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey}; -use pairing::bls12_381::Bls12; +use bls12_381::Bls12; use std::fs::File; use std::io::{self, BufReader}; use std::path::Path; diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index 68935490ad..5373bc3f9f 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -1,18 +1,14 @@ //! Abstractions over the proving system and parameters for ease of use. use bellman::groth16::{Parameters, PreparedVerifyingKey}; -use pairing::bls12_381::{Bls12, Fr}; -use zcash_primitives::{ - jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, -}; +use bls12_381::Bls12; use zcash_primitives::{ merkle_tree::MerklePath, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, prover::TxProver, redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, - JUBJUB, }; use crate::sapling::SaplingProvingContext; @@ -130,21 +126,14 @@ impl TxProver for LocalTxProver { fn spend_proof( &self, ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rseed: Rseed, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { let (proof, cv, rk) = ctx.spend_proof( proof_generation_key, diversifier, @@ -155,7 +144,6 @@ impl TxProver for LocalTxProver { merkle_path, &self.spend_params, &self.spend_vk, - &JUBJUB, )?; let mut zkproof = [0u8; GROTH_PROOF_SIZE]; @@ -169,19 +157,12 @@ impl TxProver for LocalTxProver { fn output_proof( &self, ctx: &mut Self::SaplingProvingContext, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { - let (proof, cv) = ctx.output_proof( - esk, - payment_address, - rcm, - value, - &self.output_params, - &JUBJUB, - ); + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { + let (proof, cv) = ctx.output_proof(esk, payment_address, rcm, value, &self.output_params); let mut zkproof = [0u8; GROTH_PROOF_SIZE]; proof @@ -197,6 +178,6 @@ impl TxProver for LocalTxProver { value_balance: Amount, sighash: &[u8; 32], ) -> Result { - ctx.binding_sig(value_balance, sighash, &JUBJUB) + ctx.binding_sig(value_balance, sighash) } } diff --git a/zcash_proofs/src/sapling/mod.rs b/zcash_proofs/src/sapling/mod.rs index cd37578744..ad28252fe7 100644 --- a/zcash_proofs/src/sapling/mod.rs +++ b/zcash_proofs/src/sapling/mod.rs @@ -1,10 +1,8 @@ //! Helpers for creating Sapling proofs. -use pairing::bls12_381::Bls12; -use zcash_primitives::jubjub::{ - edwards, fs::Fs, FixedGenerators, JubjubBls12, JubjubParams, Unknown, +use zcash_primitives::{ + constants::VALUE_COMMITMENT_VALUE_GENERATOR, transaction::components::Amount, }; -use zcash_primitives::transaction::components::Amount; mod prover; mod verifier; @@ -13,10 +11,7 @@ pub use self::prover::SaplingProvingContext; pub use self::verifier::SaplingVerificationContext; // This function computes `value` in the exponent of the value commitment base -fn compute_value_balance( - value: Amount, - params: &JubjubBls12, -) -> Option> { +fn compute_value_balance(value: Amount) -> Option { // Compute the absolute value (failing if -i64::MAX is // the value) let abs = match i64::from(value).checked_abs() { @@ -28,13 +23,11 @@ fn compute_value_balance( let is_negative = value.is_negative(); // Compute it in the exponent - let mut value_balance = params - .generator(FixedGenerators::ValueCommitmentValue) - .mul(Fs::from(abs), params); + let mut value_balance = VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(abs); // Negate if necessary if is_negative { - value_balance = value_balance.negate(); + value_balance = -value_balance; } // Convert to unknown order point diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 2846a54a0f..7cf4267dc8 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -2,16 +2,15 @@ use bellman::{ gadgets::multipack, groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof}, }; +use bls12_381::Bls12; use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; +use group::{Curve, GroupEncoding}; use rand_core::OsRng; use std::ops::{AddAssign, Neg}; use zcash_primitives::{ - jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown}, - primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, -}; -use zcash_primitives::{ + constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, merkle_tree::MerklePath, + primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, redjubjub::{PrivateKey, PublicKey, Signature}, sapling::Node, transaction::components::Amount, @@ -22,17 +21,17 @@ use crate::circuit::sapling::{Output, Spend}; /// A context object for creating the Sapling components of a Zcash transaction. pub struct SaplingProvingContext { - bsk: Fs, + bsk: jubjub::Fr, // (sum of the Spend value commitments) - (sum of the Output value commitments) - cv_sum: edwards::Point, + cv_sum: jubjub::ExtendedPoint, } impl SaplingProvingContext { /// Construct a new context to be used with a single transaction. pub fn new() -> Self { SaplingProvingContext { - bsk: Fs::zero(), - cv_sum: edwards::Point::zero(), + bsk: jubjub::Fr::zero(), + cv_sum: jubjub::ExtendedPoint::identity(), } } @@ -41,29 +40,21 @@ impl SaplingProvingContext { /// inside the context for later use. pub fn spend_proof( &mut self, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rseed: Rseed, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, proving_key: &Parameters, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, - ) -> Result< - ( - Proof, - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<(Proof, jubjub::ExtendedPoint, PublicKey), ()> { // Initialize secure RNG let mut rng = OsRng; // We create the randomness of the value commitment - let rcv = Fs::random(&mut rng); + let rcv = jubjub::Fr::random(&mut rng); // Accumulate the value commitment randomness in the context { @@ -75,41 +66,33 @@ impl SaplingProvingContext { } // Construct the value commitment - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, randomness: rcv, }; // Construct the viewing key - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); // Construct the payment address with the viewing key / diversifier - let payment_address = viewing_key - .to_payment_address(diversifier, params) - .ok_or(())?; + let payment_address = viewing_key.to_payment_address(diversifier).ok_or(())?; // This is the result of the re-randomization, we compute it for the caller - let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( - ar, - FixedGenerators::SpendingKeyGenerator, - params, - ); + let rk = + PublicKey(proof_generation_key.ak.clone().into()).randomize(ar, SPENDING_KEY_GENERATOR); // Let's compute the nullifier while we have the position let note = Note { value, - g_d: diversifier - .g_d::(params) - .expect("was a valid diversifier before"), + g_d: diversifier.g_d().expect("was a valid diversifier before"), pk_d: payment_address.pk_d().clone(), rseed, }; - let nullifier = note.nf(&viewing_key, merkle_path.position, params); + let nullifier = note.nf(&viewing_key, merkle_path.position); // We now have the full witness for our circuit let instance = Spend { - params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key), payment_address: Some(payment_address), @@ -129,14 +112,16 @@ impl SaplingProvingContext { // Try to verify the proof: // Construct public input for circuit - let mut public_input = [Fr::zero(); 7]; + let mut public_input = [bls12_381::Scalar::zero(); 7]; { - let (x, y) = rk.0.to_xy(); + let affine = rk.0.to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[0] = x; public_input[1] = y; } { - let (x, y) = value_commitment.cm(params).to_xy(); + let affine = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[2] = x; public_input[3] = y; } @@ -157,16 +142,10 @@ impl SaplingProvingContext { verify_proof(verifying_key, &proof, &public_input[..]).map_err(|_| ())?; // Compute value commitment - let value_commitment: edwards::Point = value_commitment.cm(params).into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into(); // Accumulate the value commitment in the context - { - let mut tmp = value_commitment.clone(); - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum += value_commitment; Ok((proof, value_commitment, rk)) } @@ -176,20 +155,19 @@ impl SaplingProvingContext { /// for later use. pub fn output_proof( &mut self, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, proving_key: &Parameters, - params: &JubjubBls12, - ) -> (Proof, edwards::Point) { + ) -> (Proof, jubjub::ExtendedPoint) { // Initialize secure RNG let mut rng = OsRng; // We construct ephemeral randomness for the value commitment. This // randomness is not given back to the caller, but the synthetic // blinding factor `bsk` is accumulated in the context. - let rcv = Fs::random(&mut rng); + let rcv = jubjub::Fr::random(&mut rng); // Accumulate the value commitment randomness in the context { @@ -201,14 +179,13 @@ impl SaplingProvingContext { } // Construct the value commitment for the proof instance - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, randomness: rcv, }; // We now have a full witness for the output proof. let instance = Output { - params, value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(rcm), @@ -220,69 +197,52 @@ impl SaplingProvingContext { create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail"); // Compute the actual value commitment - let value_commitment: edwards::Point = value_commitment.cm(params).into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into(); // Accumulate the value commitment in the context. We do this to check internal consistency. - { - let mut tmp = value_commitment.clone(); - tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum -= value_commitment; // Outputs subtract from the total. (proof, value_commitment) } /// Create the bindingSig for a Sapling transaction. All calls to spend_proof() /// and output_proof() must be completed before calling this function. - pub fn binding_sig( - &self, - value_balance: Amount, - sighash: &[u8; 32], - params: &JubjubBls12, - ) -> Result { + pub fn binding_sig(&self, value_balance: Amount, sighash: &[u8; 32]) -> Result { // Initialize secure RNG let mut rng = OsRng; // Grab the current `bsk` from the context - let bsk = PrivateKey::(self.bsk); + let bsk = PrivateKey(self.bsk); // Grab the `bvk` using DerivePublic. - let bvk = PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, params); + let bvk = PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR); // In order to check internal consistency, let's use the accumulated value // commitments (as the verifier would) and apply value_balance to compare // against our derived bvk. { // Compute value balance - let mut value_balance = compute_value_balance(value_balance, params).ok_or(())?; + let value_balance = compute_value_balance(value_balance).ok_or(())?; // Subtract value_balance from cv_sum to get final bvk - value_balance = value_balance.negate(); - let mut tmp = self.cv_sum.clone(); - tmp = tmp.add(&value_balance, params); + let final_bvk = self.cv_sum - value_balance; // The result should be the same, unless the provided valueBalance is wrong. - if bvk.0 != tmp { + if bvk.0 != final_bvk { return Err(()); } } // Construct signature message let mut data_to_be_signed = [0u8; 64]; - bvk.0 - .write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]); // Sign Ok(bsk.sign( &data_to_be_signed, &mut rng, - FixedGenerators::ValueCommitmentRandomness, - params, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, )) } } diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index 2a1fffaa1a..37bf1be3d2 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -2,31 +2,27 @@ use bellman::{ gadgets::multipack, groth16::{verify_proof, PreparedVerifyingKey, Proof}, }; -use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; -use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown}; +use bls12_381::Bls12; +use group::{Curve, GroupEncoding}; use zcash_primitives::{ + constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, redjubjub::{PublicKey, Signature}, transaction::components::Amount, }; use super::compute_value_balance; -fn is_small_order(p: &edwards::Point, params: &JubjubBls12) -> bool { - p.double(params).double(params).double(params) == edwards::Point::zero() -} - /// A context object for verifying the Sapling components of a Zcash transaction. pub struct SaplingVerificationContext { // (sum of the Spend value commitments) - (sum of the Output value commitments) - cv_sum: edwards::Point, + cv_sum: jubjub::ExtendedPoint, } impl SaplingVerificationContext { /// Construct a new context to be used with a single transaction. pub fn new() -> Self { SaplingVerificationContext { - cv_sum: edwards::Point::zero(), + cv_sum: jubjub::ExtendedPoint::identity(), } } @@ -34,61 +30,46 @@ impl SaplingVerificationContext { /// accumulating its value commitment inside the context for later use. pub fn check_spend( &mut self, - cv: edwards::Point, - anchor: Fr, + cv: jubjub::ExtendedPoint, + anchor: bls12_381::Scalar, nullifier: &[u8; 32], - rk: PublicKey, + rk: PublicKey, sighash_value: &[u8; 32], spend_auth_sig: Signature, zkproof: Proof, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, ) -> bool { - if is_small_order(&cv, params) { - return false; - } - - if is_small_order(&rk.0, params) { + if (cv.is_small_order() | rk.0.is_small_order()).into() { return false; } // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum += cv; // Grab the nullifier as a sequence of bytes let nullifier = &nullifier[..]; // Compute the signature's message for rk/spend_auth_sig let mut data_to_be_signed = [0u8; 64]; - rk.0.write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the spend_auth_sig - if !rk.verify( - &data_to_be_signed, - &spend_auth_sig, - FixedGenerators::SpendingKeyGenerator, - params, - ) { + if !rk.verify(&data_to_be_signed, &spend_auth_sig, SPENDING_KEY_GENERATOR) { return false; } // Construct public input for circuit - let mut public_input = [Fr::zero(); 7]; + let mut public_input = [bls12_381::Scalar::zero(); 7]; { - let (x, y) = rk.0.to_xy(); + let affine = rk.0.to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[0] = x; public_input[1] = y; } { - let (x, y) = cv.to_xy(); + let affine = cv.to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[2] = x; public_input[3] = y; } @@ -113,40 +94,30 @@ impl SaplingVerificationContext { /// accumulating its value commitment inside the context for later use. pub fn check_output( &mut self, - cv: edwards::Point, - cm: Fr, - epk: edwards::Point, + cv: jubjub::ExtendedPoint, + cm: bls12_381::Scalar, + epk: jubjub::ExtendedPoint, zkproof: Proof, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, ) -> bool { - if is_small_order(&cv, params) { - return false; - } - - if is_small_order(&epk, params) { + if (cv.is_small_order() | epk.is_small_order()).into() { return false; } // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum -= cv; // Construct public input for circuit - let mut public_input = [Fr::zero(); 5]; + let mut public_input = [bls12_381::Scalar::zero(); 5]; { - let (x, y) = cv.to_xy(); + let affine = cv.to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[0] = x; public_input[1] = y; } { - let (x, y) = epk.to_xy(); + let affine = epk.to_affine(); + let (x, y) = (affine.get_u(), affine.get_v()); public_input[2] = x; public_input[3] = y; } @@ -164,34 +135,29 @@ impl SaplingVerificationContext { value_balance: Amount, sighash_value: &[u8; 32], binding_sig: Signature, - params: &JubjubBls12, ) -> bool { // Obtain current cv_sum from the context let mut bvk = PublicKey(self.cv_sum.clone()); // Compute value balance - let mut value_balance = match compute_value_balance(value_balance, params) { + let value_balance = match compute_value_balance(value_balance) { Some(a) => a, None => return false, }; // Subtract value_balance from current cv_sum to get final bvk - value_balance = value_balance.negate(); - bvk.0 = bvk.0.add(&value_balance, params); + bvk.0 -= value_balance; // Compute the signature's message for bvk/binding_sig let mut data_to_be_signed = [0u8; 64]; - bvk.0 - .write(&mut data_to_be_signed[0..32]) - .expect("bvk is 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the binding_sig bvk.verify( &data_to_be_signed, &binding_sig, - FixedGenerators::ValueCommitmentRandomness, - params, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, ) } } diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs index d6646b5ce1..475e2fb0ad 100644 --- a/zcash_proofs/src/sprout.rs +++ b/zcash_proofs/src/sprout.rs @@ -4,7 +4,7 @@ use bellman::{ gadgets::multipack, groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof}, }; -use pairing::bls12_381::Bls12; +use bls12_381::Bls12; use rand_core::OsRng; use crate::circuit::sprout::*; From b9a8e1e4154e94f5a173f8e65ab429f55d2b6d21 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 Jul 2020 16:21:41 +1200 Subject: [PATCH 166/210] zcash_primitives: Remove Jubjub implementation We now use the jubjub crate for this. --- zcash_primitives/src/constants.rs | 149 ++- zcash_primitives/src/jubjub/edwards.rs | 480 -------- zcash_primitives/src/jubjub/fs.rs | 1201 --------------------- zcash_primitives/src/jubjub/mod.rs | 483 --------- zcash_primitives/src/jubjub/montgomery.rs | 317 ------ zcash_primitives/src/jubjub/tests.rs | 436 -------- zcash_primitives/src/lib.rs | 9 - zcash_proofs/src/constants.rs | 88 +- 8 files changed, 116 insertions(+), 3047 deletions(-) delete mode 100644 zcash_primitives/src/jubjub/edwards.rs delete mode 100644 zcash_primitives/src/jubjub/fs.rs delete mode 100644 zcash_primitives/src/jubjub/mod.rs delete mode 100644 zcash_primitives/src/jubjub/montgomery.rs delete mode 100644 zcash_primitives/src/jubjub/tests.rs diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index 0165f70179..ce8965af76 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -276,100 +276,161 @@ mod tests { use jubjub::SubgroupPoint; use super::*; - use crate::{ - jubjub::{FixedGenerators, JubjubParams}, - JUBJUB, - }; + use crate::group_hash::group_hash; - fn check_generator(expected: FixedGenerators, actual: SubgroupPoint) { - assert_eq!(JUBJUB.generator(expected), &actual) + fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> SubgroupPoint { + let mut tag = m.to_vec(); + let i = tag.len(); + tag.push(0u8); + + loop { + let gh = group_hash(&tag, personalization); + + // 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; + } + } } #[test] fn proof_generation_key_base_generator() { - check_generator( - FixedGenerators::ProofGenerationKey, + assert_eq!( + find_group_hash(&[], PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION), PROOF_GENERATION_KEY_GENERATOR, ); } #[test] fn note_commitment_randomness_generator() { - check_generator( - FixedGenerators::NoteCommitmentRandomness, + assert_eq!( + find_group_hash(b"r", PEDERSEN_HASH_GENERATORS_PERSONALIZATION), NOTE_COMMITMENT_RANDOMNESS_GENERATOR, ); } #[test] fn nullifier_position_generator() { - check_generator( - FixedGenerators::NullifierPosition, + assert_eq!( + find_group_hash(&[], NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION), NULLIFIER_POSITION_GENERATOR, ); } #[test] fn value_commitment_value_generator() { - check_generator( - FixedGenerators::ValueCommitmentValue, + assert_eq!( + find_group_hash(b"v", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION), VALUE_COMMITMENT_VALUE_GENERATOR, ); } #[test] fn value_commitment_randomness_generator() { - check_generator( - FixedGenerators::ValueCommitmentRandomness, + assert_eq!( + find_group_hash(b"r", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION), VALUE_COMMITMENT_RANDOMNESS_GENERATOR, ); } #[test] fn spending_key_generator() { - check_generator( - FixedGenerators::SpendingKeyGenerator, + assert_eq!( + find_group_hash(&[], SPENDING_KEY_GENERATOR_PERSONALIZATION), SPENDING_KEY_GENERATOR, ); } #[test] fn pedersen_hash_generators() { - let expected = JUBJUB.pedersen_hash_generators(); - let actual = PEDERSEN_HASH_GENERATORS; - - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual.iter()) { - assert_eq!(expected, actual); + for (m, actual) in PEDERSEN_HASH_GENERATORS.iter().enumerate() { + assert_eq!( + &find_group_hash( + &(m as u32).to_le_bytes(), + PEDERSEN_HASH_GENERATORS_PERSONALIZATION + ), + actual + ); } } #[test] - fn pedersen_hash_exp_table() { - let expected = JUBJUB.pedersen_hash_exp_table(); - let actual = &PEDERSEN_HASH_EXP_TABLE; - - // Same number of Pedersen hash generators. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual.iter()) { - // Same number of windows per generator. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same size table per window. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same table points. - assert_eq!(expected, actual); + fn no_duplicate_fixed_base_generators() { + let fixed_base_generators = [ + PROOF_GENERATION_KEY_GENERATOR, + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, + NULLIFIER_POSITION_GENERATOR, + VALUE_COMMITMENT_VALUE_GENERATOR, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + SPENDING_KEY_GENERATOR, + ]; + + // Check for duplicates, far worse than spec inconsistencies! + for (i, p1) in fixed_base_generators.iter().enumerate() { + if p1.is_identity().into() { + panic!("Neutral element!"); + } + + for p2 in fixed_base_generators.iter().skip(i + 1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + } + } + } + + /// Check for simple relations between the generators, that make finding collisions easy; + /// far worse than spec inconsistencies! + fn check_consistency_of_pedersen_hash_generators( + pedersen_hash_generators: &[jubjub::SubgroupPoint], + ) { + for (i, p1) in pedersen_hash_generators.iter().enumerate() { + if p1.is_identity().into() { + panic!("Neutral element!"); + } + for p2 in pedersen_hash_generators.iter().skip(i + 1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + if *p1 == -p2 { + panic!("Inverse generator!"); + } + } + + // check for a generator being the sum of any other two + for (j, p2) in pedersen_hash_generators.iter().enumerate() { + if j == i { + continue; + } + for (k, p3) in pedersen_hash_generators.iter().enumerate() { + if k == j || k == i { + continue; + } + let sum = p2 + p3; + if sum == *p1 { + panic!("Linear relation between generators!"); + } } } } } #[test] - fn pedersen_hash_chunks_per_generator() { - assert_eq!( - JUBJUB.pedersen_hash_chunks_per_generator(), - PEDERSEN_HASH_CHUNKS_PER_GENERATOR - ); + fn pedersen_hash_generators_consistency() { + check_consistency_of_pedersen_hash_generators(PEDERSEN_HASH_GENERATORS); + } + + #[test] + #[should_panic(expected = "Linear relation between generators!")] + fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { + let mut pedersen_hash_generators = PEDERSEN_HASH_GENERATORS.to_vec(); + + // Test for linear relation + pedersen_hash_generators.push(PEDERSEN_HASH_GENERATORS[0] + PEDERSEN_HASH_GENERATORS[1]); + + check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); } } diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs deleted file mode 100644 index 612fbf56d7..0000000000 --- a/zcash_primitives/src/jubjub/edwards.rs +++ /dev/null @@ -1,480 +0,0 @@ -use ff::{BitIterator, Field, PrimeField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; - -use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; - -use rand_core::RngCore; - -use std::marker::PhantomData; - -use std::io::{self, Read, Write}; - -// 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 { - x: E::Fr, - y: E::Fr, - t: E::Fr, - z: E::Fr, - _marker: PhantomData, -} - -fn convert_subgroup(from: &Point) -> Point { - Point { - x: from.x, - y: from.y, - t: from.t, - z: from.z, - _marker: PhantomData, - } -} - -impl From<&Point> for Point { - fn from(p: &Point) -> Point { - p.clone() - } -} - -impl From> for Point { - fn from(p: Point) -> Point { - convert_subgroup(&p) - } -} - -impl From<&Point> for Point { - fn from(p: &Point) -> Point { - convert_subgroup(p) - } -} - -impl Clone for Point { - fn clone(&self) -> Self { - convert_subgroup(self) - } -} - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> 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 Point { - pub fn read(mut reader: R, params: &E::Params) -> io::Result { - let mut y_repr = ::Repr::default(); - reader.read_exact(y_repr.as_mut())?; - - let x_sign = (y_repr.as_ref()[31] >> 7) == 1; - y_repr.as_mut()[31] &= 0x7f; - - match E::Fr::from_repr(y_repr) { - Some(y) => { - let p = Self::get_for_y(y, x_sign, params); - if bool::from(p.is_some()) { - Ok(p.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) - } - } - None => 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) -> CtOption { - // 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.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()); - - tmp2.invert().and_then(|tmp2| { - // tmp1 = (y^2 - 1) / (dy^2 + 1) - tmp1.mul_assign(&tmp2); - - tmp1.sqrt().map(|mut x| { - if x.is_odd() != sign { - x = x.neg(); - } - - let mut t = x; - t.mul_assign(&y); - - Point { - x, - y, - t, - z: E::Fr::one(), - _marker: PhantomData, - } - }) - }) - } - - /// This guarantees the point is in the prime order subgroup - #[must_use] - pub fn mul_by_cofactor(&self, params: &E::Params) -> Point { - let tmp = self.double(params).double(params).double(params); - - convert_subgroup(&tmp) - } - - pub fn rand(rng: &mut R, params: &E::Params) -> Self { - loop { - let y = E::Fr::random(rng); - let sign = rng.next_u32() % 2 != 0; - - let p = Self::get_for_y(y, sign, params); - if bool::from(p.is_some()) { - return p.unwrap(); - } - } - } -} - -impl Point { - pub fn write(&self, mut writer: W) -> io::Result<()> { - let (x, y) = self.to_xy(); - - assert_eq!(E::Fr::NUM_BITS, 255); - - let mut y_repr = y.to_repr(); - if x.is_odd() { - y_repr.as_mut()[31] |= 0x80; - } - - writer.write_all(y_repr.as_ref()) - } - - /// Convert from a Montgomery point - pub fn from_montgomery(m: &montgomery::Point, params: &E::Params) -> Self { - match m.to_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. - Point { - x: E::Fr::zero(), - y: E::Fr::one().neg(), - 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, - 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> { - 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, - } - } - - /// Convert to affine coordinates - pub fn to_xy(&self) -> (E::Fr, E::Fr) { - let zinv = self.z.invert().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 = p.x.neg(); - p.t = p.t.neg(); - - 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 a = self.x.square(); - - // B = Y1^2 - let b = self.y.square(); - - // C = 2*Z1^2 - let c = self.z.square().double(); - - // D = a*A - // = -A - let d = a.neg(); - - // E = (X1+Y1)^2 - A - B - let mut e = self.x; - e.add_assign(&self.y); - e = 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(); - 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::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 - } -} diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs deleted file mode 100644 index d7e164efa4..0000000000 --- a/zcash_primitives/src/jubjub/fs.rs +++ /dev/null @@ -1,1201 +0,0 @@ -use byteorder::{ByteOrder, LittleEndian}; -use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PrimeField}; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use super::ToUniform; - -// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 -const MODULUS: FsRepr = FsRepr([ - 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, - 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, -]); - -const MODULUS_LIMBS: Fs = Fs([ - 0xd0970e5ed6f72cb7, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9, -]); - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 252; - -// The number of bits that must be shaved from the beginning of -// the representation when randomly sampling. -const REPR_SHAVE_BITS: u32 = 4; - -// R = 2**256 % s -const R: Fs = Fs([ - 0x25f80bb3b99607d9, - 0xf315d62f66b6e750, - 0x932514eeeb8814f4, - 0x9a6fc6f479155c6, -]); - -// R2 = R^2 % s -const R2: Fs = Fs([ - 0x67719aa495e57731, - 0x51b0cef09ce3fc26, - 0x69dab7fac026e9a5, - 0x4f6547b8d127688, -]); - -// INV = -(s^{-1} mod 2^64) mod s -const INV: u64 = 0x1ba3a358ef788ef9; - -// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: Fs = Fs([ - 0x720b1b19d49ea8f1, - 0xbf4aa36101f13a58, - 0x5fa8cc968193ccbb, - 0xe70cbdc7dccf3ac, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 1; - -// 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: Fs = Fs([ - 0xaa9f02ab1d6124de, - 0xb3524a6466112932, - 0x7342261215ac260b, - 0x4d6b87b1da259e2, -]); - -// -((2**256) mod s) mod s -const NEGATIVE_ONE: Fs = Fs([ - 0xaa9f02ab1d6124de, - 0xb3524a6466112932, - 0x7342261215ac260b, - 0x4d6b87b1da259e2, -]); - -/// This is the underlying representation of an element of `Fs`. -#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] -pub struct FsRepr(pub [u8; 32]); - -impl ::std::fmt::Display for FsRepr { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:02x}", *i)?; - } - - Ok(()) - } -} - -impl AsRef<[u8]> for FsRepr { - #[inline(always)] - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for FsRepr { - #[inline(always)] - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -/// This is an element of the scalar field of the Jubjub curve. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Fs([u64; 4]); - -impl Default for Fs { - fn default() -> Self { - Fs::zero() - } -} - -impl ConstantTimeEq for Fs { - fn ct_eq(&self, other: &Fs) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl ::std::fmt::Display for Fs { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fs({})", self.to_repr()) - } -} - -impl From for Fs { - #[inline(always)] - fn from(val: u64) -> Fs { - let mut raw = [0u64; 4]; - raw[0] = val; - Fs(raw) * R2 - } -} - -impl From for FsRepr { - fn from(e: Fs) -> FsRepr { - e.to_repr() - } -} - -impl<'a> From<&'a Fs> for FsRepr { - fn from(e: &'a Fs) -> FsRepr { - e.to_repr() - } -} - -impl ConditionallySelectable for Fs { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fs([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - ]) - } -} - -impl Neg for Fs { - type Output = Self; - - #[inline] - fn neg(mut self) -> Self { - if !self.is_zero() { - let mut tmp = MODULUS_LIMBS; - tmp.sub_noborrow(&self); - self = tmp; - } - self - } -} - -impl<'r> Add<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn add(self, other: &Self) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } -} - -impl Add for Fs { - type Output = Self; - - #[inline] - fn add(self, other: Self) -> Self { - self + &other - } -} - -impl<'r> AddAssign<&'r Fs> for Fs { - #[inline] - fn add_assign(&mut self, other: &Self) { - // This cannot exceed the backing capacity. - self.add_nocarry(&other); - - // However, it may need to be reduced. - self.reduce(); - } -} - -impl AddAssign for Fs { - #[inline] - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn sub(self, other: &Self) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } -} - -impl Sub for Fs { - type Output = Self; - - #[inline] - fn sub(self, other: Self) -> Self { - self - &other - } -} - -impl<'r> SubAssign<&'r Fs> for Fs { - #[inline] - fn sub_assign(&mut self, other: &Self) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.cmp_native(self) == ::core::cmp::Ordering::Greater { - self.add_nocarry(&MODULUS_LIMBS); - } - - self.sub_noborrow(&other); - } -} - -impl SubAssign for Fs { - #[inline] - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fs { - type Output = Self; - - #[inline] - fn mul(self, other: Self) -> Self { - self * &other - } -} - -impl<'r> MulAssign<&'r Fs> for Fs { - #[inline] - fn mul_assign(&mut self, other: &Self) { - let mut carry = 0; - let r0 = mac_with_carry(0, self.0[0], other.0[0], &mut carry); - let r1 = mac_with_carry(0, self.0[0], other.0[1], &mut carry); - let r2 = mac_with_carry(0, self.0[0], other.0[2], &mut carry); - let r3 = mac_with_carry(0, self.0[0], other.0[3], &mut carry); - let r4 = carry; - let mut carry = 0; - let r1 = mac_with_carry(r1, self.0[1], other.0[0], &mut carry); - let r2 = mac_with_carry(r2, self.0[1], other.0[1], &mut carry); - let r3 = mac_with_carry(r3, self.0[1], other.0[2], &mut carry); - let r4 = mac_with_carry(r4, self.0[1], other.0[3], &mut carry); - let r5 = carry; - let mut carry = 0; - let r2 = mac_with_carry(r2, self.0[2], other.0[0], &mut carry); - let r3 = mac_with_carry(r3, self.0[2], other.0[1], &mut carry); - let r4 = mac_with_carry(r4, self.0[2], other.0[2], &mut carry); - let r5 = mac_with_carry(r5, self.0[2], other.0[3], &mut carry); - let r6 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, self.0[3], other.0[0], &mut carry); - let r4 = mac_with_carry(r4, self.0[3], other.0[1], &mut carry); - let r5 = mac_with_carry(r5, self.0[3], other.0[2], &mut carry); - let r6 = mac_with_carry(r6, self.0[3], other.0[3], &mut carry); - let r7 = carry; - self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); - } -} - -impl MulAssign for Fs { - #[inline] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl PrimeField for Fs { - type Repr = FsRepr; - type ReprEndianness = byteorder::LittleEndian; - - fn from_repr(r: FsRepr) -> Option { - let r = { - let mut inner = [0; 4]; - LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); - Fs(inner) - }; - - if r.is_valid() { - Some(r * &R2) - } else { - None - } - } - - fn to_repr(&self) -> FsRepr { - let mut r = *self; - r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut repr = [0; 32]; - LittleEndian::write_u64_into(&r.0, &mut repr[..]); - FsRepr(repr) - } - - #[inline(always)] - fn is_odd(&self) -> bool { - let mut r = *self; - r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - r.0[0] & 1 == 1 - } - - fn char() -> FsRepr { - MODULUS - } - - const NUM_BITS: u32 = MODULUS_BITS; - - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -impl Field for Fs { - fn random(rng: &mut R) -> Self { - loop { - let mut tmp = { - let mut repr = [0u64; 4]; - for limb in &mut repr { - *limb = rng.next_u64(); - } - Fs(repr) - }; - - // Mask away the unused most-significant bits. - tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; - - if tmp.is_valid() { - return tmp; - } - } - } - - #[inline] - fn zero() -> Self { - Fs::from(0) - } - - #[inline] - fn one() -> Self { - R - } - - #[inline] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline] - fn double(&self) -> Self { - let mut ret = *self; - - // This cannot exceed the backing capacity. - let mut last = 0; - for i in &mut ret.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - - // However, it may need to be reduced. - ret.reduce(); - - ret - } - - fn invert(&self) -> CtOption { - // We need to find b such that b * a ≡ 1 mod p. As we are in a prime - // field, we can apply Fermat's Little Theorem: - // - // a^p ≡ a mod p - // a^(p-1) ≡ 1 mod p - // a^(p-2) * a ≡ 1 mod p - // - // Thus inversion can be implemented with a single exponentiation. - let inverse = self.pow_vartime(&[ - 0xd097_0e5e_d6f7_2cb5u64, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]); - - CtOption::new(inverse, Choice::from(if self.is_zero() { 0 } else { 1 })) - } - - #[inline] - fn square(&self) -> Self { - let mut carry = 0; - let r1 = mac_with_carry(0, self.0[0], self.0[1], &mut carry); - let r2 = mac_with_carry(0, self.0[0], self.0[2], &mut carry); - let r3 = mac_with_carry(0, self.0[0], self.0[3], &mut carry); - let r4 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, self.0[1], self.0[2], &mut carry); - let r4 = mac_with_carry(r4, self.0[1], self.0[3], &mut carry); - let r5 = carry; - let mut carry = 0; - let r5 = mac_with_carry(r5, self.0[2], self.0[3], &mut carry); - let r6 = carry; - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let mut carry = 0; - let r0 = mac_with_carry(0, self.0[0], self.0[0], &mut carry); - let r1 = adc(r1, 0, &mut carry); - let r2 = mac_with_carry(r2, self.0[1], self.0[1], &mut carry); - let r3 = adc(r3, 0, &mut carry); - let r4 = mac_with_carry(r4, self.0[2], self.0[2], &mut carry); - let r5 = adc(r5, 0, &mut carry); - let r6 = mac_with_carry(r6, self.0[3], self.0[3], &mut carry); - let r7 = adc(r7, 0, &mut carry); - - let mut ret = *self; - ret.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); - ret - } - - fn sqrt(&self) -> CtOption { - // Shank's algorithm for s mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) - - // a1 = self^((s - 3) // 4) - let mut a1 = self.pow_vartime([ - 0xb425c397b5bdcb2du64, - 0x299a0824f3320420, - 0x4199cec0404d0ec0, - 0x39f6d3a994cebea, - ]); - let mut a0 = a1.square(); - a0.mul_assign(self); - a1.mul_assign(self); - - CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) - } -} - -impl Fs { - /// Compares two elements in native representation. This is only used - /// internally. - #[inline(always)] - fn cmp_native(&self, other: &Fs) -> ::std::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::std::cmp::Ordering::Less; - } else if a > b { - return ::std::cmp::Ordering::Greater; - } - } - - ::std::cmp::Ordering::Equal - } - - /// Determines if the element is really in the field. This is only used - /// internally. - #[inline(always)] - fn is_valid(&self) -> bool { - // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use - // this internal function to eliminate the cycle. - self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &Fs) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &Fs) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = sbb(*a, *b, &mut borrow); - } - } - - /// Subtracts the modulus from this element if this element is not in the - /// field. Only used internally. - #[inline(always)] - fn reduce(&mut self) { - if !self.is_valid() { - self.sub_noborrow(&MODULUS_LIMBS); - } - } - - #[inline(always)] - fn mont_reduce( - &mut self, - r0: u64, - mut r1: u64, - mut r2: u64, - mut r3: u64, - mut r4: u64, - mut r5: u64, - mut r6: u64, - mut r7: u64, - ) { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r0, k, MODULUS_LIMBS.0[0], &mut carry); - r1 = mac_with_carry(r1, k, MODULUS_LIMBS.0[1], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[2], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[3], &mut carry); - r4 = adc(r4, 0, &mut carry); - let carry2 = carry; - let k = r1.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r1, k, MODULUS_LIMBS.0[0], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[1], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[2], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[3], &mut carry); - r5 = adc(r5, carry2, &mut carry); - let carry2 = carry; - let k = r2.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r2, k, MODULUS_LIMBS.0[0], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[1], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[2], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[3], &mut carry); - r6 = adc(r6, carry2, &mut carry); - let carry2 = carry; - let k = r3.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r3, k, MODULUS_LIMBS.0[0], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[1], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[2], &mut carry); - r6 = mac_with_carry(r6, k, MODULUS_LIMBS.0[3], &mut carry); - r7 = adc(r7, carry2, &mut carry); - self.0[0] = r4; - self.0[1] = r5; - self.0[2] = r6; - self.0[3] = r7; - self.reduce(); - } - - fn mul_bits>(&self, bits: BitIterator) -> Self { - let mut res = Self::zero(); - for bit in bits { - res = res.double(); - - if bit { - res.add_assign(self) - } - } - res - } -} - -impl ToUniform for Fs { - /// Convert a little endian byte string into a uniform - /// field element. The number is reduced mod s. The caller - /// is responsible for ensuring the input is 64 bytes of - /// Random Oracle output. - fn to_uniform(digest: &[u8]) -> Self { - assert_eq!(digest.len(), 64); - Self::one().mul_bits(BitIterator::::new(digest)) - } -} - -#[test] -fn test_neg_one() { - let o = Fs::one().neg(); - - assert_eq!(NEGATIVE_ONE, o); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fs_is_valid() { - let mut a = MODULUS_LIMBS; - assert!(!a.is_valid()); - a.sub_noborrow(&Fs([1, 0, 0, 0])); - assert!(a.is_valid()); - assert!(Fs::zero().is_valid()); - assert!(Fs([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 - ]) - .is_valid()); - assert!(!Fs([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - .is_valid()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fs::random(&mut rng); - assert!(a.is_valid()); - } -} - -#[test] -fn test_fs_add_assign() { - { - // Random number - let mut tmp = Fs::from_str( - "4577408157467272683998459759522778614363623736323078995109579213719612604198", - ) - .unwrap(); - assert!(tmp.is_valid()); - // Test that adding zero has no effect. - tmp.add_assign(&Fs::zero()); - assert_eq!( - tmp, - Fs([ - 0x8e6bfff4722d6e67, - 0x5643da5c892044f9, - 0x9465f4b281921a69, - 0x25f752d3edd7162 - ]) - ); - // Add one and test for the result. - tmp.add_assign(&Fs([1, 0, 0, 0])); - assert_eq!( - tmp, - Fs([ - 0x8e6bfff4722d6e68, - 0x5643da5c892044f9, - 0x9465f4b281921a69, - 0x25f752d3edd7162 - ]) - ); - // Add another random number that exercises the reduction. - tmp.add_assign(&Fs([ - 0xb634d07bc42d4a70, - 0xf724f0c008411f5f, - 0x456d4053d865af34, - 0x24ce814e8c63027, - ])); - assert_eq!( - tmp, - Fs([ - 0x44a0d070365ab8d8, - 0x4d68cb1c91616459, - 0xd9d3350659f7c99e, - 0x4ac5d4227a3a189 - ]) - ); - // Add one to (s - 1) and test for the result. - tmp = Fs([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9, - ]); - tmp.add_assign(&Fs([1, 0, 0, 0])); - assert!(tmp.is_zero()); - // Add a random number to another one such that the result is s - 1 - tmp = Fs([ - 0xa11fda5950ce3636, - 0x922e0dbccfe0ca0e, - 0xacebb6e215b82d4a, - 0x97ffb8cdc3aee93, - ]); - tmp.add_assign(&Fs([ - 0x2f7734058628f680, - 0x143a12d6fce74674, - 0x597b841eeb7c0db6, - 0x4fdb95d88f8c115, - ])); - assert_eq!( - tmp, - Fs([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 - ]) - ); - // Add one to the result and test for it. - tmp.add_assign(&Fs([1, 0, 0, 0])); - assert!(tmp.is_zero()); - } - - // Test associativity - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fs::random(&mut rng); - let b = Fs::random(&mut rng); - let c = Fs::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - - let mut tmp2 = b; - tmp2.add_assign(&c); - tmp2.add_assign(&a); - - assert!(tmp1.is_valid()); - assert!(tmp2.is_valid()); - assert_eq!(tmp1, tmp2); - } -} - -#[test] -fn test_fs_sub_assign() { - { - // Test arbitrary subtraction that tests reduction. - let mut tmp = Fs([ - 0xb384d9f6877afd99, - 0x4442513958e1a1c1, - 0x352c4b8a95eccc3f, - 0x2db62dee4b0f2, - ]); - tmp.sub_assign(&Fs([ - 0xec5bd2d13ed6b05a, - 0x2adc0ab3a39b5fa, - 0x82d3360a493e637e, - 0x53ccff4a64d6679, - ])); - assert_eq!( - tmp, - Fs([ - 0x97c015841f9b79f6, - 0xe7fcb121eb6ffc49, - 0xb8c050814de2a3c1, - 0x943c0589dcafa21 - ]) - ); - - // Test the opposite subtraction which doesn't test reduction. - tmp = Fs([ - 0xec5bd2d13ed6b05a, - 0x2adc0ab3a39b5fa, - 0x82d3360a493e637e, - 0x53ccff4a64d6679, - ]); - tmp.sub_assign(&Fs([ - 0xb384d9f6877afd99, - 0x4442513958e1a1c1, - 0x352c4b8a95eccc3f, - 0x2db62dee4b0f2, - ])); - assert_eq!( - tmp, - Fs([ - 0x38d6f8dab75bb2c1, - 0xbe6b6f71e1581439, - 0x4da6ea7fb351973e, - 0x539f491c768b587 - ]) - ); - - // Test for sensible results with zero - tmp = Fs::zero(); - tmp.sub_assign(&Fs::from(0)); - assert!(tmp.is_zero()); - - tmp = Fs([ - 0x361e16aef5cce835, - 0x55bbde2536e274c1, - 0x4dc77a63fd15ee75, - 0x1e14bb37c14f230, - ]); - tmp.sub_assign(&Fs::from(0)); - assert_eq!( - tmp, - Fs([ - 0x361e16aef5cce835, - 0x55bbde2536e274c1, - 0x4dc77a63fd15ee75, - 0x1e14bb37c14f230 - ]) - ); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fs::random(&mut rng); - let b = Fs::random(&mut rng); - - let mut tmp1 = a; - tmp1.sub_assign(&b); - - let mut tmp2 = b; - tmp2.sub_assign(&a); - - tmp1.add_assign(&tmp2); - assert!(tmp1.is_zero()); - } -} - -#[test] -fn test_fs_mul_assign() { - let mut tmp = Fs([ - 0xb433b01287f71744, - 0x4eafb86728c4d108, - 0xfdd52c14b9dfbe65, - 0x2ff1f3434821118, - ]); - tmp.mul_assign(&Fs([ - 0xdae00fc63c9fa90f, - 0x5a5ed89b96ce21ce, - 0x913cd26101bd6f58, - 0x3f0822831697fe9, - ])); - assert!( - tmp == Fs([ - 0xb68ecb61d54d2992, - 0x5ff95874defce6a6, - 0x3590eb053894657d, - 0x53823a118515933 - ]) - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fs::random(&mut rng); - let b = Fs::random(&mut rng); - let c = Fs::random(&mut rng); - - let mut tmp1 = a; - tmp1.mul_assign(&b); - tmp1.mul_assign(&c); - - let mut tmp2 = b; - tmp2.mul_assign(&c); - tmp2.mul_assign(&a); - - assert_eq!(tmp1, tmp2); - } - - for _ in 0..1000000 { - // Ensure that r * (a + b + c) = r*a + r*b + r*c - - let r = Fs::random(&mut rng); - let mut a = Fs::random(&mut rng); - let mut b = Fs::random(&mut rng); - let mut c = Fs::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - tmp1.mul_assign(&r); - - a.mul_assign(&r); - b.mul_assign(&r); - c.mul_assign(&r); - - a.add_assign(&b); - a.add_assign(&c); - - assert_eq!(tmp1, a); - } -} - -#[test] -fn test_fs_squaring() { - let a = Fs([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xe7db4ea6533afa8, - ]); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fs::from_repr(FsRepr([ - 0xaa, 0xfb, 0x52, 0xbc, 0x5c, 0xf5, 0xc7, 0x12, 0x9e, 0xce, 0xe6, 0xb5, 0xa0, 0x98, - 0xdc, 0xde, 0x6a, 0x39, 0xa5, 0x26, 0x27, 0x89, 0xd2, 0x0a, 0xb3, 0x77, 0xee, 0x8f, - 0xaf, 0x82, 0xfe, 0x09, - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fs::random(&mut rng); - - let tmp = a.square(); - - let mut tmp2 = a; - tmp2.mul_assign(&a); - - assert_eq!(tmp, tmp2); - } -} - -#[test] -fn test_fs_invert() { - assert!(bool::from(Fs::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fs::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fs::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fs_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fs::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fs_neg() { - { - let a = Fs::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fs::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fs_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0u64..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fs::random(&mut rng); - let target = a.pow_vartime(&[i]); - let mut c = Fs::one(); - for _ in 0..i { - c.mul_assign(&a); - } - assert_eq!(c, target); - } - - use byteorder::ByteOrder; - let mut char_limbs = [0; 4]; - byteorder::LittleEndian::read_u64_into(Fs::char().as_ref(), &mut char_limbs); - - for _ in 0..1000 { - // Exponentiating by the modulus should have no effect in a prime field. - let a = Fs::random(&mut rng); - - assert_eq!(a, a.pow_vartime(char_limbs)); - } -} - -#[test] -fn test_fs_sqrt() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fs::zero().sqrt().unwrap(), Fs::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fs::random(&mut rng); - let nega = a.neg(); - let b = a.square(); - - let b = b.sqrt().unwrap(); - - assert!(a == b || nega == b); - } - - for _ in 0..1000 { - // Ensure sqrt(a)^2 = a for random a - let a = Fs::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[test] -fn test_fs_from_to_repr() { - // r + 1 should not be in the field - assert!(Fs::from_repr(FsRepr([ - 0xb8, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, - 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, - 0x7d, 0x0e, - ])) - .is_none()); - - // r should not be in the field - assert!(Fs::from_repr(Fs::char()).is_none()); - - // Multiply some arbitrary representations to see if the result is as expected. - let mut a_fs = Fs::from_repr(FsRepr([ - 0x71, 0x7b, 0x33, 0xd0, 0x05, 0x0c, 0x2d, 0x5f, 0x79, 0x04, 0xa2, 0xf8, 0xb0, 0xf2, 0x1d, - 0x0a, 0x63, 0xb8, 0x1b, 0xe7, 0x85, 0x37, 0xd7, 0x0a, 0xec, 0xac, 0xc9, 0x80, 0x04, 0xa0, - 0x04, 0x05, - ])) - .unwrap(); - let b_fs = Fs::from_repr(FsRepr([ - 0x62, 0x75, 0x47, 0x1e, 0xf5, 0x6f, 0x35, 0x66, 0x03, 0x76, 0xcf, 0x55, 0xab, 0x92, 0x0a, - 0x06, 0x92, 0xd1, 0x4d, 0x36, 0xc7, 0x73, 0x42, 0x8e, 0xc5, 0x4d, 0x34, 0x4a, 0x84, 0xf8, - 0x6d, 0x03, - ])) - .unwrap(); - let c_fs = Fs::from_repr(FsRepr([ - 0x68, 0x28, 0x4f, 0x8f, 0x70, 0x61, 0xef, 0x7e, 0xfb, 0x46, 0x29, 0xf5, 0x6c, 0x7e, 0x7a, - 0x74, 0x17, 0x00, 0x12, 0xc9, 0xd7, 0x75, 0xdd, 0x83, 0xf7, 0x3d, 0x0f, 0x7f, 0x17, 0xf5, - 0x62, 0x07, - ])) - .unwrap(); - a_fs.mul_assign(&b_fs); - assert_eq!(a_fs, c_fs); - - // Zero should be in the field. - assert!(Fs::from_repr(FsRepr::default()).unwrap().is_zero()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fs elements into representations and back again, and compare. - let a = Fs::random(&mut rng); - let a_repr = a.to_repr(); - let b_repr = FsRepr::from(a); - assert_eq!(a_repr, b_repr); - let a_again = Fs::from_repr(a_repr).unwrap(); - - assert_eq!(a, a_again); - } -} - -#[test] -fn test_fs_display() { - assert_eq!( - format!( - "{}", - Fs::from_repr(FsRepr([ - 0xa3, 0x01, 0x8a, 0x99, 0xb9, 0xef, 0x28, 0x55, 0x89, 0x70, 0x35, 0xcb, 0xd5, 0xad, - 0xd2, 0x5b, 0x98, 0x1f, 0x49, 0xdb, 0x6a, 0xfa, 0x61, 0xc0, 0xd9, 0x03, 0xdb, 0x43, - 0xd1, 0xb9, 0x0d, 0x07, - ])) - .unwrap() - ), - "Fs(0x070db9d143db03d9c061fa6adb491f985bd2add5cb3570895528efb9998a01a3)".to_string() - ); - assert_eq!( - format!( - "{}", - Fs::from_repr(FsRepr([ - 0x9e, 0x99, 0x17, 0x27, 0x5e, 0x74, 0x74, 0xd6, 0x38, 0xf3, 0x96, 0x3e, 0x2d, 0xf5, - 0xb1, 0xbe, 0xb9, 0x82, 0x94, 0x54, 0x47, 0xe1, 0x7a, 0x9c, 0x22, 0x0d, 0x53, 0x24, - 0x60, 0x70, 0x99, 0x09, - ])) - .unwrap() - ), - "Fs(0x0999706024530d229c7ae147549482b9beb1f52d3e96f338d674745e2717999e)".to_string() - ); -} - -#[test] -fn test_fs_num_bits() { - assert_eq!(Fs::NUM_BITS, 252); - assert_eq!(Fs::CAPACITY, 251); -} - -#[test] -fn test_fs_root_of_unity() { - assert_eq!(Fs::S, 1); - assert_eq!(Fs::multiplicative_generator(), Fs::from(6)); - assert_eq!( - Fs::multiplicative_generator().pow_vartime([ - 0x684b872f6b7b965bu64, - 0x53341049e6640841, - 0x83339d80809a1d80, - 0x73eda753299d7d4 - ]), - Fs::root_of_unity() - ); - assert_eq!(Fs::root_of_unity().pow_vartime([1u64 << Fs::S]), Fs::one()); - assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none())); -} diff --git a/zcash_primitives/src/jubjub/mod.rs b/zcash_primitives/src/jubjub/mod.rs deleted file mode 100644 index 1ab04e504d..0000000000 --- a/zcash_primitives/src/jubjub/mod.rs +++ /dev/null @@ -1,483 +0,0 @@ -//! The [Jubjub] curve for efficient elliptic curve operations in circuits built -//! over [BLS12-381]. -//! -//! 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. -//! -//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub -//! [BLS12-381]: pairing::bls12_381 - -use ff::PrimeField; -use group::{Curve, Group}; -use pairing::Engine; - -use crate::group_hash::group_hash; - -use crate::constants; - -/// 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; - /// The parameters of Jubjub and the Sapling protocol - type Params: JubjubParams; -} - -/// The pre-computed parameters for Jubjub, including curve -/// constants and various limits and window tables. -pub trait JubjubParams: 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) -> &[jubjub::SubgroupPoint]; - /// Returns the exp table for Pedersen hashes. - fn pedersen_hash_exp_table(&self) -> &[Vec>]; - /// Returns the maximum number of chunks per segment of the Pedersen hash. - fn pedersen_hash_chunks_per_generator(&self) -> usize; - - /// 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) -> &jubjub::SubgroupPoint; - /// 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_381::Bls12 { - type Fs = jubjub::Fr; - type Params = JubjubBls12; -} - -pub struct JubjubBls12 { - edwards_d: bls12_381::Scalar, - montgomery_a: bls12_381::Scalar, - montgomery_2a: bls12_381::Scalar, - scale: bls12_381::Scalar, - - pedersen_hash_generators: Vec, - pedersen_hash_exp: Vec>>, - - fixed_base_generators: Vec, - fixed_base_circuit_generators: Vec>>, -} - -impl JubjubParams for JubjubBls12 { - fn edwards_d(&self) -> &bls12_381::Scalar { - &self.edwards_d - } - fn montgomery_a(&self) -> &bls12_381::Scalar { - &self.montgomery_a - } - fn montgomery_2a(&self) -> &bls12_381::Scalar { - &self.montgomery_2a - } - fn scale(&self) -> &bls12_381::Scalar { - &self.scale - } - fn pedersen_hash_generators(&self) -> &[jubjub::SubgroupPoint] { - &self.pedersen_hash_generators - } - fn pedersen_hash_exp_table(&self) -> &[Vec>] { - &self.pedersen_hash_exp - } - fn pedersen_hash_chunks_per_generator(&self) -> usize { - 63 - } - fn fixed_base_chunks_per_generator(&self) -> usize { - 84 - } - fn generator(&self, base: FixedGenerators) -> &jubjub::SubgroupPoint { - &self.fixed_base_generators[base as usize] - } - fn circuit_generators( - &self, - base: FixedGenerators, - ) -> &[Vec<(bls12_381::Scalar, bls12_381::Scalar)>] { - &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 = bls12_381::Scalar::from_str("40962").unwrap(); - let montgomery_2a = montgomery_a.double(); - - let mut tmp_params = JubjubBls12 { - // d = -(10240/10241) - edwards_d: bls12_381::Scalar::from_str( - "19257038036680949359750312669786877991949435402254120286184196891950884077233", - ) - .unwrap(), - // A = 40962 - montgomery_a, - // 2A = 2.A - montgomery_2a, - // scaling factor = sqrt(4 / (a - d)) - scale: bls12_381::Scalar::from_str( - "17814886934372412843466061268024708274627479829237077604635722030778476050649", - ) - .unwrap(), - - // We'll initialize these below - pedersen_hash_generators: vec![], - pedersen_hash_exp: vec![], - fixed_base_generators: vec![], - fixed_base_circuit_generators: vec![], - }; - - // Create the bases for the Pedersen hashes - { - let mut pedersen_hash_generators = vec![]; - - for m in 0..6 { - use byteorder::{LittleEndian, WriteBytesExt}; - - let mut segment_number = [0u8; 4]; - (&mut segment_number[0..4]) - .write_u32::(m) - .unwrap(); - - pedersen_hash_generators.push(JubjubBls12::find_group_hash( - &segment_number, - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - )); - } - - JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); - 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 <= jubjub::Fr::NUM_BITS { - let mut table = Vec::with_capacity(1 << window); - - let mut base = jubjub::SubgroupPoint::identity(); - - for _ in 0..(1 << window) { - table.push(base.clone()); - base += g; - } - - tables.push(table); - num_bits += window; - - for _ in 0..window { - g = g.double(); - } - } - - 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![jubjub::SubgroupPoint::identity(); FixedGenerators::Max as usize]; - - fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = - JubjubBls12::find_group_hash( - &[], - constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, - ); - - fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = - JubjubBls12::find_group_hash( - b"r", - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - ); - - fixed_base_generators[FixedGenerators::NullifierPosition as usize] = - JubjubBls12::find_group_hash( - &[], - constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, - ); - - fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = - JubjubBls12::find_group_hash( - b"v", - constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - ); - - fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = - JubjubBls12::find_group_hash( - b"r", - constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - ); - - fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = - JubjubBls12::find_group_hash( - &[], - constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, - ); - - // Check for duplicates, far worse than spec inconsistencies! - for (i, p1) in fixed_base_generators.iter().enumerate() { - if p1.is_identity().into() { - 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 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![(bls12_381::Scalar::zero(), bls12_381::Scalar::one())]; - let mut g = gen.clone(); - for _ in 0..7 { - let g_affine = jubjub::ExtendedPoint::from(g).to_affine(); - coeffs.push((g_affine.get_u(), g_affine.get_v())); - g += gen; - } - 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 - } - - fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> jubjub::SubgroupPoint { - let mut tag = m.to_vec(); - let i = tag.len(); - tag.push(0u8); - - loop { - let gh = group_hash(&tag, personalization); - - // 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; - } - } - } - - /// Check for simple relations between the generators, that make finding collisions easy; - /// far worse than spec inconsistencies! - fn check_consistency_of_pedersen_hash_generators( - pedersen_hash_generators: &[jubjub::SubgroupPoint], - ) { - for (i, p1) in pedersen_hash_generators.iter().enumerate() { - if p1.is_identity().into() { - panic!("Neutral element!"); - } - for p2 in pedersen_hash_generators.iter().skip(i + 1) { - if p1 == p2 { - panic!("Duplicate generator!"); - } - if *p1 == -p2 { - panic!("Inverse generator!"); - } - } - - // check for a generator being the sum of any other two - for (j, p2) in pedersen_hash_generators.iter().enumerate() { - if j == i { - continue; - } - for (k, p3) in pedersen_hash_generators.iter().enumerate() { - if k == j || k == i { - continue; - } - let sum = p2 + p3; - if sum == *p1 { - panic!("Linear relation between generators!"); - } - } - } - } - } -} - -#[test] -fn test_jubjub_bls12() { - use bls12_381::Bls12; - use hex_literal::hex; - - let params = JubjubBls12::new(); - - tests::test_suite::(¶ms); - - let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); - let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); - let q = edwards::Point::::get_for_y( - bls12_381::Scalar::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::::read(&test_repr[..], ¶ms).unwrap(); - let q = edwards::Point::::get_for_y( - bls12_381::Scalar::from_str( - "22440861827555040311190986994816762244378363690614952020532787748720529117853", - ) - .unwrap(), - true, - ¶ms, - ) - .unwrap(); - - assert!(p == q); -} - -#[test] -#[should_panic(expected = "Linear relation between generators!")] -fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { - let mut pedersen_hash_generators: Vec = vec![]; - - use byteorder::{LittleEndian, WriteBytesExt}; - - for m in 0..5 { - let mut segment_number = [0u8; 4]; - (&mut segment_number[0..4]) - .write_u32::(m) - .unwrap(); - - let p = JubjubBls12::find_group_hash( - &segment_number, - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - ); - pedersen_hash_generators.push(p); - } - - let p1 = pedersen_hash_generators[0].clone(); - let p2 = pedersen_hash_generators[1].clone(); - - //test for linear relation - pedersen_hash_generators.push(p1 + p2); - - JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); -} diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs deleted file mode 100644 index 4b568021da..0000000000 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ /dev/null @@ -1,317 +0,0 @@ -use ff::{BitIterator, Field, PrimeField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; - -use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; - -use rand_core::RngCore; - -use std::marker::PhantomData; - -// Represents the affine point (X, Y) -pub struct Point { - x: E::Fr, - y: E::Fr, - infinity: bool, - _marker: PhantomData, -} - -fn convert_subgroup(from: &Point) -> Point { - Point { - x: from.x, - y: from.y, - infinity: from.infinity, - _marker: PhantomData, - } -} - -impl From> for Point { - fn from(p: Point) -> Point { - convert_subgroup(&p) - } -} - -impl Clone for Point { - fn clone(&self) -> Self { - convert_subgroup(self) - } -} - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> 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 Point { - pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption { - // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) - - let mut x2 = x.square(); - - let mut rhs = x2; - rhs.mul_assign(params.montgomery_a()); - rhs.add_assign(&x); - x2.mul_assign(&x); - rhs.add_assign(&x2); - - rhs.sqrt().map(|mut y| { - if y.is_odd() != sign { - y = y.neg(); - } - - Point { - x, - y, - infinity: false, - _marker: PhantomData, - } - }) - } - - /// This guarantees the point is in the prime order subgroup - #[must_use] - pub fn mul_by_cofactor(&self, params: &E::Params) -> Point { - let tmp = self.double(params).double(params).double(params); - - convert_subgroup(&tmp) - } - - pub fn rand(rng: &mut R, params: &E::Params) -> Self { - loop { - let x = E::Fr::random(rng); - let sign = rng.next_u32() % 2 != 0; - - let p = Self::get_for_x(x, sign, params); - if p.is_some().into() { - return p.unwrap(); - } - } - } -} - -impl Point { - /// Convert from an Edwards point - pub fn from_edwards(e: &edwards::Point, params: &E::Params) -> Self { - let (x, y) = e.to_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.invert().unwrap()) - } - - let mut v = u; - v.mul_assign(&x.invert().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> { - 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 to_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 = p.y.neg(); - - 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(); - tmp.mul_assign(&self.x); - tmp = tmp.double(); - delta.add_assign(&tmp); - } - { - let mut tmp = self.x.square(); - delta.add_assign(&tmp); - tmp = tmp.double(); - delta.add_assign(&tmp); - } - { - let tmp = self.y.double(); - // y is nonzero so this must be nonzero - delta.mul_assign(&tmp.invert().unwrap()); - } - - let mut x3 = delta.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 = y3.neg(); - - 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); - // self.x != other.x, so this must be nonzero - delta.mul_assign(&tmp.invert().unwrap()); - } - - let mut x3 = delta.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 = y3.neg(); - - Point { - x: x3, - y: y3, - infinity: false, - _marker: PhantomData, - } - } - } - } - } - - #[must_use] - pub fn mul::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 - } -} diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs deleted file mode 100644 index a8b5274690..0000000000 --- a/zcash_primitives/src/jubjub/tests.rs +++ /dev/null @@ -1,436 +0,0 @@ -use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; - -use ff::{Endianness, Field, PrimeField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub fn test_suite(params: &E::Params) { - test_back_and_forth::(params); - test_jubjub_params::(params); - test_rand::(params); - test_get_for::(params); - test_identities::(params); - test_addition_associativity::(params); - test_order::(params); - test_mul_associativity::(params); - test_loworder::(params); - test_read_write::(params); -} - -fn is_on_mont_curve>(x: E::Fr, y: E::Fr, params: &P) -> bool { - let lhs = y.square(); - - let x2 = x.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>( - x: E::Fr, - y: E::Fr, - params: &P, -) -> bool { - let x2 = x.square(); - - let y2 = y.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(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::::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(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::::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.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - - let (x, y) = res2.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - - let (x, y) = res3.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - } -} - -fn test_order(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::::zero() - .as_prime_order(params) - .is_some()); - - for _ in 0..50 { - // Pick a random point and multiply it by the cofactor - let base = Point::::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::::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(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::::rand(rng, params); - let b = Point::::rand(rng, params); - let c = Point::::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::::rand(rng, params); - let b = Point::::rand(rng, params); - let c = Point::::rand(rng, params); - - assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms)); - } -} - -fn test_identities(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::::zero(); - assert!(z.double(¶ms) == z); - assert!(z.negate() == z); - - for _ in 0..100 { - let r = Point::::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::::zero(); - assert!(z.double(¶ms) == z); - assert!(z.negate() == z); - - for _ in 0..100 { - let r = Point::::rand(rng, params); - - assert!(r.add(&Point::zero(), ¶ms) == r); - assert!(r.add(&r.negate(), ¶ms) == Point::zero()); - } - } -} - -fn test_get_for(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; - - let p = edwards::Point::::get_for_y(y, sign, params); - if bool::from(p.is_some()) { - let mut p = p.unwrap(); - assert!(p.to_xy().0.is_odd() == sign); - p = p.negate(); - assert!(edwards::Point::::get_for_y(y, !sign, params).unwrap() == p); - } - } -} - -fn test_read_write(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::::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(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::::rand(rng, params); - let e = edwards::Point::::rand(rng, params); - - { - let (x, y) = p.to_xy().unwrap(); - assert!(is_on_mont_curve(x, y, params)); - } - - { - let (x, y) = e.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - } - } -} - -fn test_back_and_forth(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::::rand(rng, params); - let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params); - let mont_p2 = montgomery::Point::::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(params: &E::Params) { - // a = -1 - let a = E::Fr::one().neg(); - - { - // Check that 2A is consistent with A - assert_eq!(¶ms.montgomery_a().double(), params.montgomery_2a()); - } - - { - // The twisted Edwards addition law is complete when d is nonsquare - // and a is square. - - assert!(bool::from(params.edwards_d().sqrt().is_none())); - assert!(bool::from(a.sqrt().is_some())); - } - - { - // Other convenient sanity checks regarding d - - // tmp = d - let mut tmp = *params.edwards_d(); - - // 1 / d is nonsquare - assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); - - // tmp = -d - tmp = tmp.neg(); - - // -d is nonsquare - assert!(bool::from(tmp.sqrt().is_none())); - - // 1 / -d is nonsquare - assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); - } - - { - // Check that A^2 - 4 is nonsquare: - let mut tmp = params.montgomery_a().square(); - tmp.sub_assign(&E::Fr::from_str("4").unwrap()); - assert!(bool::from(tmp.sqrt().is_none())); - } - - { - // Check that A - 2 is nonsquare: - let mut tmp = params.montgomery_a().clone(); - tmp.sub_assign(&E::Fr::from_str("2").unwrap()); - assert!(bool::from(tmp.sqrt().is_none())); - } - - { - // Check the validity of the scaling factor - let mut tmp = a; - tmp.sub_assign(¶ms.edwards_d()); - tmp = tmp.invert().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(); - - let max = { - // Grab char - 1 in little endian. - let mut tmp = (-E::Fs::one()).to_repr(); - ::ReprEndianness::toggle_little_endian(&mut tmp); - - // Shift right by 1 bit. - let mut borrow = 0; - for b in tmp.as_mut().iter_mut().rev() { - let new_borrow = *b & 1; - *b = (borrow << 7) | (*b >> 1); - borrow = new_borrow; - } - - // Turns out we want this in little endian! - tmp - }; - - let mut pacc = E::Fs::zero(); - let mut nacc = E::Fs::zero(); - - for _ in 0..params.pedersen_hash_chunks_per_generator() { - // tmp = cur * 4 - let tmp = cur.double().double(); - - pacc += &tmp; - nacc -= &tmp; // The first subtraction wraps intentionally. - - let mut pacc_repr = pacc.to_repr(); - let mut nacc_repr = nacc.to_repr(); - ::ReprEndianness::toggle_little_endian(&mut pacc_repr); - ::ReprEndianness::toggle_little_endian(&mut nacc_repr); - - fn less_than(val: &[u8], bound: &[u8]) -> bool { - for (a, b) in val.iter().rev().zip(bound.iter().rev()) { - if a < b { - return true; - } - } - - false - } - assert!(less_than(pacc_repr.as_ref(), max.as_ref())); - assert!(less_than(pacc_repr.as_ref(), nacc_repr.as_ref())); - - // cur = cur * 16 - for _ in 0..4 { - cur = cur.double(); - } - } - } - - { - // 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); - } -} diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index fefbad07e2..d20a4ace9d 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -6,13 +6,10 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use lazy_static::lazy_static; - pub mod block; pub mod consensus; pub mod constants; pub mod group_hash; -pub mod jubjub; pub mod keys; pub mod legacy; pub mod merkle_tree; @@ -29,9 +26,3 @@ pub mod zip32; #[cfg(test)] mod test_vectors; - -use crate::jubjub::JubjubBls12; - -lazy_static! { - pub static ref JUBJUB: JubjubBls12 = JubjubBls12::new(); -} diff --git a/zcash_proofs/src/constants.rs b/zcash_proofs/src/constants.rs index 769e2ed62e..5727ae385d 100644 --- a/zcash_proofs/src/constants.rs +++ b/zcash_proofs/src/constants.rs @@ -168,97 +168,31 @@ fn generate_pedersen_circuit_generators() -> Vec>> { #[cfg(test)] mod tests { - use zcash_primitives::{ - jubjub::{FixedGenerators, JubjubParams}, - JUBJUB, - }; + use ff::PrimeField; use super::*; - fn check_generator(expected: FixedGenerators, actual: FixedGenerator) { - let expected = JUBJUB.circuit_generators(expected); - - // Same number of windows per generator. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same size table per window. - assert_eq!(expected.len(), actual.len()); - for (expected, actual) in expected.iter().zip(actual) { - // Same coordinates. - assert_eq!(expected.0, actual.0); - assert_eq!(expected.1, actual.1); - } - } - } - #[test] fn edwards_d() { - assert_eq!(*JUBJUB.edwards_d(), EDWARDS_D); + // d = -(10240/10241) + assert_eq!( + -Scalar::from_str("10240").unwrap() + * Scalar::from_str("10241").unwrap().invert().unwrap(), + EDWARDS_D + ); } #[test] fn montgomery_a() { - assert_eq!(*JUBJUB.montgomery_a(), MONTGOMERY_A); + assert_eq!(Scalar::from_str("40962").unwrap(), MONTGOMERY_A); } #[test] fn montgomery_scale() { - assert_eq!(*JUBJUB.scale(), MONTGOMERY_SCALE); - } - - #[test] - fn fixed_base_chunks_per_generator() { + // scaling factor = sqrt(4 / (a - d)) assert_eq!( - JUBJUB.fixed_base_chunks_per_generator(), - FIXED_BASE_CHUNKS_PER_GENERATOR - ); - } - - #[test] - fn proof_generation_key_base_generator() { - check_generator( - FixedGenerators::ProofGenerationKey, - &PROOF_GENERATION_KEY_GENERATOR, - ); - } - - #[test] - fn note_commitment_randomness_generator() { - check_generator( - FixedGenerators::NoteCommitmentRandomness, - &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, - ); - } - - #[test] - fn nullifier_position_generator() { - check_generator( - FixedGenerators::NullifierPosition, - &NULLIFIER_POSITION_GENERATOR, - ); - } - - #[test] - fn value_commitment_value_generator() { - check_generator( - FixedGenerators::ValueCommitmentValue, - &VALUE_COMMITMENT_VALUE_GENERATOR, - ); - } - - #[test] - fn value_commitment_randomness_generator() { - check_generator( - FixedGenerators::ValueCommitmentRandomness, - &VALUE_COMMITMENT_RANDOMNESS_GENERATOR, - ); - } - - #[test] - fn spending_key_generator() { - check_generator( - FixedGenerators::SpendingKeyGenerator, - &SPENDING_KEY_GENERATOR, + MONTGOMERY_SCALE.square() * (-Scalar::one() - EDWARDS_D), + Scalar::from(4), ); } } From f735e8b83bb502af577fec297937d81b50087bb6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 Jul 2020 16:22:14 +1200 Subject: [PATCH 167/210] pairing: Remove BLS12-381 implementation It is replaced by the bls12_381 crate. --- bellman/Cargo.toml | 1 + bellman/src/domain.rs | 6 +- bellman/src/gadgets/blake2s.rs | 18 +- bellman/src/gadgets/boolean.rs | 62 +- bellman/src/gadgets/lookup.rs | 16 +- bellman/src/gadgets/multipack.rs | 4 +- bellman/src/gadgets/num.rs | 58 +- bellman/src/gadgets/sha256.rs | 8 +- bellman/src/gadgets/test/mod.rs | 22 +- bellman/src/gadgets/uint32.rs | 12 +- bellman/src/groth16/mod.rs | 6 +- bellman/src/multiexp.rs | 12 +- bellman/tests/mimc.rs | 8 +- pairing/Cargo.toml | 4 - pairing/benches/bls12_381/ec.rs | 173 -- pairing/benches/bls12_381/fq.rs | 209 -- pairing/benches/bls12_381/fq12.rs | 125 - pairing/benches/bls12_381/fq2.rs | 146 -- pairing/benches/bls12_381/fr.rs | 209 -- pairing/benches/bls12_381/mod.rs | 118 - pairing/benches/pairing_benches.rs | 12 - pairing/src/bls12_381/README.md | 71 - pairing/src/bls12_381/ec.rs | 2199 ----------------- pairing/src/bls12_381/fq.rs | 1824 -------------- pairing/src/bls12_381/fq12.rs | 286 --- pairing/src/bls12_381/fq2.rs | 925 ------- pairing/src/bls12_381/fq6.rs | 478 ---- pairing/src/bls12_381/fr.rs | 601 ----- pairing/src/bls12_381/mod.rs | 524 ---- .../g1_compressed_valid_test_vectors.dat | Bin 48000 -> 0 bytes .../g1_uncompressed_invalid_test_vectors.dat | 0 .../g1_uncompressed_valid_test_vectors.dat | Bin 96000 -> 0 bytes .../g2_compressed_valid_test_vectors.dat | Bin 96000 -> 0 bytes .../g2_uncompressed_valid_test_vectors.dat | Bin 192000 -> 0 bytes pairing/src/bls12_381/tests/mod.rs | 645 ----- pairing/src/lib.rs | 2 - 36 files changed, 120 insertions(+), 8664 deletions(-) delete mode 100644 pairing/benches/bls12_381/ec.rs delete mode 100644 pairing/benches/bls12_381/fq.rs delete mode 100644 pairing/benches/bls12_381/fq12.rs delete mode 100644 pairing/benches/bls12_381/fq2.rs delete mode 100644 pairing/benches/bls12_381/fr.rs delete mode 100644 pairing/benches/bls12_381/mod.rs delete mode 100644 pairing/benches/pairing_benches.rs delete mode 100644 pairing/src/bls12_381/README.md delete mode 100644 pairing/src/bls12_381/ec.rs delete mode 100644 pairing/src/bls12_381/fq.rs delete mode 100644 pairing/src/bls12_381/fq12.rs delete mode 100644 pairing/src/bls12_381/fq2.rs delete mode 100644 pairing/src/bls12_381/fq6.rs delete mode 100644 pairing/src/bls12_381/fr.rs delete mode 100644 pairing/src/bls12_381/mod.rs delete mode 100644 pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat delete mode 100644 pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat delete mode 100644 pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat delete mode 100644 pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat delete mode 100644 pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat delete mode 100644 pairing/src/bls12_381/tests/mod.rs diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 09b7720ebd..45fd968ff7 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -24,6 +24,7 @@ byteorder = "1" subtle = "2.2.1" [dev-dependencies] +bls12_381 = { version = "0.1", path = "../bls12_381" } hex-literal = "0.2" rand = "0.7" rand_xorshift = "0.2" diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 098b0edda9..91a8b83552 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -375,7 +375,7 @@ fn parallel_fft>( #[cfg(feature = "pairing")] #[test] fn polynomial_arith() { - use pairing::bls12_381::Fr; + use bls12_381::Scalar as Fr; use rand_core::RngCore; fn test_mul(rng: &mut R) { @@ -422,7 +422,7 @@ fn polynomial_arith() { #[cfg(feature = "pairing")] #[test] fn fft_composition() { - use pairing::bls12_381::Fr; + use bls12_381::Scalar as Fr; use rand_core::RngCore; fn test_comp(rng: &mut R) { @@ -460,7 +460,7 @@ fn fft_composition() { #[cfg(feature = "pairing")] #[test] fn parallel_fft_consistency() { - use pairing::bls12_381::Fr; + use bls12_381::Scalar as Fr; use rand_core::RngCore; use std::cmp::min; diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs index 226563d427..f5e46eaae4 100644 --- a/bellman/src/gadgets/blake2s.rs +++ b/bellman/src/gadgets/blake2s.rs @@ -408,8 +408,8 @@ pub fn blake2s>( #[cfg(test)] mod test { use blake2s_simd::Params as Blake2sParams; + use bls12_381::Scalar; use hex_literal::hex; - use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -420,7 +420,7 @@ mod test { #[test] fn test_blank_hash() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits = vec![]; let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); assert!(cs.is_satisfied()); @@ -443,7 +443,7 @@ mod test { #[test] fn test_blake2s_constraints() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits: Vec<_> = (0..512) .map(|i| { AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) @@ -461,7 +461,7 @@ mod test { // Test that 512 fixed leading bits (constants) // doesn't result in more constraints. - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -481,7 +481,7 @@ mod test { #[test] fn test_blake2s_constant_constraints() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -512,7 +512,7 @@ mod test { let hash_result = h.finalize(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -559,7 +559,7 @@ mod test { let data: Vec = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); assert_eq!(data.len(), 256); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -596,7 +596,7 @@ mod test { let data: Vec = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); assert_eq!(data.len(), 700); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; @@ -651,7 +651,7 @@ mod test { let hash_result = h.finalize(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index 940790d9c0..e5e24b6b69 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -749,19 +749,19 @@ mod test { use super::{field_into_allocated_bits_le, u64_into_boolean_vec_le, AllocatedBit, Boolean}; use crate::gadgets::test::*; use crate::ConstraintSystem; + use bls12_381::Scalar; use ff::{Field, PrimeField}; - use pairing::bls12_381::Fr; #[test] fn test_allocated_bit() { let mut cs = TestConstraintSystem::new(); AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - assert!(cs.get("boolean") == Fr::one()); + assert!(cs.get("boolean") == Scalar::one()); assert!(cs.is_satisfied()); - cs.set("boolean", Fr::zero()); + cs.set("boolean", Scalar::zero()); assert!(cs.is_satisfied()); - cs.set("boolean", Fr::from_str("2").unwrap()); + cs.set("boolean", Scalar::from_str("2").unwrap()); assert!(!cs.is_satisfied()); assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); } @@ -770,7 +770,7 @@ mod test { fn test_xor() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); @@ -806,7 +806,7 @@ mod test { fn test_and() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); @@ -842,7 +842,7 @@ mod test { fn test_and_not() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); @@ -878,7 +878,7 @@ mod test { fn test_nor() { for a_val in [false, true].iter() { for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); @@ -917,7 +917,7 @@ mod test { for a_neg in [false, true].iter().cloned() { for b_neg in [false, true].iter().cloned() { { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::from( AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), @@ -938,7 +938,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::Constant(a_bool); let mut b = Boolean::from( @@ -957,7 +957,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::from( AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), @@ -976,7 +976,7 @@ mod test { assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); } { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut a = Boolean::Constant(a_bool); let mut b = Boolean::Constant(b_bool); @@ -1005,7 +1005,7 @@ mod test { #[test] fn test_boolean_negation() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); @@ -1097,7 +1097,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a; let b; @@ -1306,7 +1306,7 @@ mod test { for first_operand in variants.iter().cloned() { for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a; let b; @@ -1527,7 +1527,7 @@ mod test { #[test] fn test_u64_into_boolean_vec_le() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); @@ -1548,9 +1548,9 @@ mod test { #[test] fn test_field_into_allocated_bits_le() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); - let r = Fr::from_str( + let r = Scalar::from_str( "9147677615426976802526883532204139322118074541891858454835346926874644257775", ) .unwrap(); @@ -1643,16 +1643,16 @@ mod test { } else { assert_eq!(cs.get("ch"), { if expected { - Fr::one() + Scalar::one() } else { - Fr::zero() + Scalar::zero() } }); cs.set("ch", { if expected { - Fr::zero() + Scalar::zero() } else { - Fr::one() + Scalar::one() } }); assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); @@ -1735,16 +1735,16 @@ mod test { } else { assert_eq!(cs.get("maj"), { if expected { - Fr::one() + Scalar::one() } else { - Fr::zero() + Scalar::zero() } }); cs.set("maj", { if expected { - Fr::zero() + Scalar::zero() } else { - Fr::one() + Scalar::one() } }); assert_eq!(cs.which_is_unsatisfied().unwrap(), "maj computation"); @@ -1757,7 +1757,7 @@ mod test { #[test] fn test_alloc_conditionally() { { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); let value = None; @@ -1773,7 +1773,7 @@ mod test { { // since value is true, b must be false, so it should succeed - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let value = Some(true); let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); @@ -1790,7 +1790,7 @@ mod test { { // since value is true, b must be false, so it should fail - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let value = Some(true); let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); @@ -1805,7 +1805,7 @@ mod test { let value = Some(false); //check with false bit - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) .unwrap(); @@ -1813,7 +1813,7 @@ mod test { assert!(cs.is_satisfied()); //check with true bit - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) .unwrap(); diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs index d7fb301518..2ef6f697c8 100644 --- a/bellman/src/gadgets/lookup.rs +++ b/bellman/src/gadgets/lookup.rs @@ -191,8 +191,8 @@ mod test { use crate::gadgets::boolean::{AllocatedBit, Boolean}; use crate::gadgets::test::*; + use bls12_381::Scalar; use ff::Field; - use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use std::ops::{AddAssign, Neg}; @@ -218,8 +218,8 @@ mod test { let bits = vec![a, b, c]; - let points: Vec<(Fr, Fr)> = (0..8) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) + let points: Vec<(Scalar, Scalar)> = (0..8) + .map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng))) .collect(); let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); @@ -263,8 +263,8 @@ mod test { let bits = vec![a, b, c]; - let points: Vec<(Fr, Fr)> = (0..4) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) + let points: Vec<(Scalar, Scalar)> = (0..4) + .map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng))) .collect(); let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); @@ -297,15 +297,15 @@ mod test { let window_size = 4; - let mut assignment = vec![Fr::zero(); 1 << window_size]; + let mut assignment = vec![Scalar::zero(); 1 << window_size]; let constants: Vec<_> = (0..(1 << window_size)) - .map(|_| Fr::random(&mut rng)) + .map(|_| Scalar::random(&mut rng)) .collect(); synth(window_size, &constants, &mut assignment); for b in 0..(1 << window_size) { - let mut acc = Fr::zero(); + let mut acc = Scalar::zero(); for j in 0..(1 << window_size) { if j & b == j { diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs index b4df2eeab1..fda716e679 100644 --- a/bellman/src/gadgets/multipack.rs +++ b/bellman/src/gadgets/multipack.rs @@ -74,7 +74,7 @@ pub fn compute_multipacking(bits: &[bool]) -> Vec { #[test] fn test_multipacking() { use crate::ConstraintSystem; - use pairing::bls12_381::Fr; + use bls12_381::Scalar; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -87,7 +87,7 @@ fn test_multipacking() { ]); for num_bits in 0..1500 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let bits: Vec = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect(); diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index 7f94d93ea7..f0267579d3 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -410,8 +410,8 @@ impl Num { #[cfg(test)] mod test { use crate::ConstraintSystem; + use bls12_381::Scalar; use ff::{BitIterator, Field, PrimeField}; - use pairing::bls12_381::Fr; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Neg, SubAssign}; @@ -423,22 +423,22 @@ mod test { fn test_allocated_num() { let mut cs = TestConstraintSystem::new(); - AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap(); + AllocatedNum::alloc(&mut cs, || Ok(Scalar::one())).unwrap(); - assert!(cs.get("num") == Fr::one()); + assert!(cs.get("num") == Scalar::one()); } #[test] fn test_num_squaring() { let mut cs = TestConstraintSystem::new(); - let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); + let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap(); let n2 = n.square(&mut cs).unwrap(); assert!(cs.is_satisfied()); - assert!(cs.get("squared num") == Fr::from_str("9").unwrap()); - assert!(n2.value.unwrap() == Fr::from_str("9").unwrap()); - cs.set("squared num", Fr::from_str("10").unwrap()); + assert!(cs.get("squared num") == Scalar::from_str("9").unwrap()); + assert!(n2.value.unwrap() == Scalar::from_str("9").unwrap()); + cs.set("squared num", Scalar::from_str("10").unwrap()); assert!(!cs.is_satisfied()); } @@ -446,16 +446,16 @@ mod test { fn test_num_multiplication() { let mut cs = TestConstraintSystem::new(); - let n = - AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); - let n2 = - AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap(); + let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::from_str("12").unwrap())) + .unwrap(); + let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::from_str("10").unwrap())) + .unwrap(); let n3 = n.mul(&mut cs, &n2).unwrap(); assert!(cs.is_satisfied()); - assert!(cs.get("product num") == Fr::from_str("120").unwrap()); - assert!(n3.value.unwrap() == Fr::from_str("120").unwrap()); - cs.set("product num", Fr::from_str("121").unwrap()); + assert!(cs.get("product num") == Scalar::from_str("120").unwrap()); + assert!(n3.value.unwrap() == Scalar::from_str("120").unwrap()); + cs.set("product num", Scalar::from_str("121").unwrap()); assert!(!cs.is_satisfied()); } @@ -468,8 +468,10 @@ mod test { { let mut cs = TestConstraintSystem::new(); - let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); - let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); + let a = + AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap(); + let b = + AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap(); let condition = Boolean::constant(false); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); @@ -482,8 +484,10 @@ mod test { { let mut cs = TestConstraintSystem::new(); - let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); - let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); + let a = + AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap(); + let b = + AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap(); let condition = Boolean::constant(true); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); @@ -499,24 +503,24 @@ mod test { { let mut cs = TestConstraintSystem::new(); - let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); + let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap(); n.assert_nonzero(&mut cs).unwrap(); assert!(cs.is_satisfied()); - cs.set("ephemeral inverse", Fr::from_str("3").unwrap()); + cs.set("ephemeral inverse", Scalar::from_str("3").unwrap()); assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint")); } { let mut cs = TestConstraintSystem::new(); - let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap(); + let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::zero())).unwrap(); assert!(n.assert_nonzero(&mut cs).is_err()); } } #[test] fn test_into_bits_strict() { - let negone = Fr::one().neg(); + let negone = Scalar::one().neg(); let mut cs = TestConstraintSystem::new(); @@ -526,7 +530,7 @@ mod test { assert!(cs.is_satisfied()); // make the bit representation the characteristic - cs.set("bit 254/boolean", Fr::one()); + cs.set("bit 254/boolean", Scalar::one()); // this makes the conditional boolean constraint fail assert_eq!( @@ -543,7 +547,7 @@ mod test { ]); for i in 0..200 { - let r = Fr::random(&mut rng); + let r = Scalar::random(&mut rng); let mut cs = TestConstraintSystem::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); @@ -567,15 +571,15 @@ mod test { } } - cs.set("num", Fr::random(&mut rng)); + cs.set("num", Scalar::random(&mut rng)); assert!(!cs.is_satisfied()); cs.set("num", r); assert!(cs.is_satisfied()); - for i in 0..Fr::NUM_BITS { + for i in 0..Scalar::NUM_BITS { let name = format!("bit {}/boolean", i); let cur = cs.get(&name); - let mut tmp = Fr::one(); + let mut tmp = Scalar::one(); tmp.sub_assign(&cur); cs.set(&name, tmp); assert!(!cs.is_satisfied()); diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs index 8f5f66dfe2..4ffc03254d 100644 --- a/bellman/src/gadgets/sha256.rs +++ b/bellman/src/gadgets/sha256.rs @@ -273,8 +273,8 @@ mod test { use super::*; use crate::gadgets::boolean::AllocatedBit; use crate::gadgets::test::TestConstraintSystem; + use bls12_381::Scalar; use hex_literal::hex; - use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -282,7 +282,7 @@ mod test { fn test_blank_hash() { let iv = get_sha256_iv(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); input_bits[0] = Boolean::Constant(true); let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap(); @@ -312,7 +312,7 @@ mod test { let iv = get_sha256_iv(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let input_bits: Vec<_> = (0..512) .map(|i| { Boolean::from( @@ -346,7 +346,7 @@ mod test { h.update(&data); let hash_result = h.finalize(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let mut input_bits = vec![]; for (byte_i, input_byte) in data.into_iter().enumerate() { diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index c1395b10b9..08f5391eda 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -418,46 +418,46 @@ impl ConstraintSystem for TestConstraintSystem::one(); + let one = TestConstraintSystem::::one(); cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); assert!(!cs.is_satisfied()); assert!(cs.which_is_unsatisfied() == Some("mult")); - assert!(cs.get("product") == Fr::from_str("40").unwrap()); + assert!(cs.get("product") == Scalar::from_str("40").unwrap()); - cs.set("product", Fr::from_str("16").unwrap()); + cs.set("product", Scalar::from_str("16").unwrap()); assert!(cs.is_satisfied()); { let mut cs = cs.namespace(|| "test1"); let mut cs = cs.namespace(|| "test2"); - cs.alloc(|| "hehe", || Ok(Fr::one())).unwrap(); + cs.alloc(|| "hehe", || Ok(Scalar::one())).unwrap(); } - assert!(cs.get("test1/test2/hehe") == Fr::one()); + assert!(cs.get("test1/test2/hehe") == Scalar::one()); } diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs index a040a2bd6d..db5e2d506f 100644 --- a/bellman/src/gadgets/uint32.rs +++ b/bellman/src/gadgets/uint32.rs @@ -401,8 +401,8 @@ mod test { use crate::gadgets::multieq::MultiEq; use crate::gadgets::test::*; use crate::ConstraintSystem; + use bls12_381::Scalar; use ff::Field; - use pairing::bls12_381::Fr; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -484,7 +484,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -529,7 +529,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -572,7 +572,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -685,7 +685,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); @@ -729,7 +729,7 @@ mod test { ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let a = rng.next_u32(); let b = rng.next_u32(); diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 41859fbd2e..462dee38b5 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -479,8 +479,8 @@ mod test_with_bls12_381 { use super::*; use crate::{Circuit, ConstraintSystem, SynthesisError}; + use bls12_381::{Bls12, Scalar}; use ff::{Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; use rand::thread_rng; use std::ops::MulAssign; @@ -537,8 +537,8 @@ mod test_with_bls12_381 { let pvk = prepare_verifying_key::(¶ms.vk); for _ in 0..100 { - let a = Fr::random(rng); - let b = Fr::random(rng); + let a = Scalar::random(rng); + let b = Scalar::random(rng); let mut c = a; c.mul_assign(&b); diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index efbe02e7e3..b70a2ea643 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -311,17 +311,19 @@ fn test_with_bls12() { acc } + use bls12_381::{Bls12, Scalar}; use group::{Curve, Group}; - use pairing::{ - bls12_381::{Bls12, Fr}, - Engine, - }; + use pairing::Engine; use rand; const SAMPLES: usize = 1 << 14; let rng = &mut rand::thread_rng(); - let v = Arc::new((0..SAMPLES).map(|_| Fr::random(rng)).collect::>()); + let v = Arc::new( + (0..SAMPLES) + .map(|_| Scalar::random(rng)) + .collect::>(), + ); let g = Arc::new( (0..SAMPLES) .map(|_| ::G1::random(rng).to_affine()) diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs index 269690840c..e8cb9219bc 100644 --- a/bellman/tests/mimc.rs +++ b/bellman/tests/mimc.rs @@ -8,7 +8,7 @@ use std::time::{Duration, Instant}; use ff::{Field, PrimeField}; // We're going to use the BLS12-381 pairing-friendly elliptic curve. -use pairing::bls12_381::{Bls12, Fr}; +use bls12_381::{Bls12, Scalar}; // We'll use these interfaces to construct our circuit. use bellman::{Circuit, ConstraintSystem, SynthesisError}; @@ -151,7 +151,7 @@ fn test_mimc() { // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS) - .map(|_| Fr::random(rng)) + .map(|_| Scalar::random(rng)) .collect::>(); println!("Creating parameters..."); @@ -183,8 +183,8 @@ fn test_mimc() { for _ in 0..SAMPLES { // Generate a random preimage and compute the image - let xl = Fr::random(rng); - let xr = Fr::random(rng); + let xl = Scalar::random(rng); + let xr = Scalar::random(rng); let image = mimc(xl, xr, &constants); proof_vec.truncate(0); diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml index 5bd5e1e2be..d24e2d0ed0 100644 --- a/pairing/Cargo.toml +++ b/pairing/Cargo.toml @@ -32,9 +32,5 @@ unstable-features = ["expose-arith"] expose-arith = [] default = [] -[[bench]] -name = "pairing_benches" -harness = false - [badges] maintenance = { status = "actively-developed" } diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs deleted file mode 100644 index 41c2b4cb10..0000000000 --- a/pairing/benches/bls12_381/ec.rs +++ /dev/null @@ -1,173 +0,0 @@ -pub(crate) mod g1 { - use criterion::{criterion_group, Criterion}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::AddAssign; - - use ff::Field; - use group::Group; - use pairing::bls12_381::*; - - fn bench_g1_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, Fr)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G1::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp *= v[count].1; - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g1_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, G1)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G1::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G1::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g1_add_assign_mixed(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, G1Affine)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G1::random(&mut rng).into())) - .collect(); - - let mut count = 0; - c.bench_function("G1::add_assign_mixed", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - criterion_group!( - benches, - bench_g1_add_assign, - bench_g1_add_assign_mixed, - bench_g1_mul_assign, - ); -} - -pub(crate) mod g2 { - use criterion::{criterion_group, Criterion}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::AddAssign; - - use ff::Field; - use group::Group; - use pairing::bls12_381::*; - - fn bench_g2_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, Fr)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G2::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp *= v[count].1; - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g2_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, G2)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), G2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G2::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g2_add_assign_mixed(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, G2Affine)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), G2::random(&mut rng).into())) - .collect(); - - let mut count = 0; - c.bench_function("G2::add_assign_mixed", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - criterion_group!( - benches, - bench_g2_add_assign, - bench_g2_add_assign_mixed, - bench_g2_mul_assign, - ); -} diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs deleted file mode 100644 index 417ec9f8c8..0000000000 --- a/pairing/benches/bls12_381/fq.rs +++ /dev/null @@ -1,209 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use ff::{Field, PrimeField}; -use pairing::bls12_381::*; - -fn bench_fq_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_square(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::invert", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].invert() - }) - }); -} - -fn bench_fq_neg(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::neg", |b| { - b.iter(|| { - let tmp = v[count].neg(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).square()) - .collect(); - - let mut count = 0; - c.bench_function("Fq::sqrt", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() - }) - }); -} - -fn bench_fq_to_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::to_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].to_repr() - }) - }); -} - -fn bench_fq_from_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).to_repr()) - .collect(); - - let mut count = 0; - c.bench_function("Fq::from_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - Fq::from_repr(v[count]) - }) - }); -} - -criterion_group!( - benches, - bench_fq_add_assign, - bench_fq_sub_assign, - bench_fq_mul_assign, - bench_fq_square, - bench_fq_invert, - bench_fq_neg, - bench_fq_sqrt, - bench_fq_to_repr, - bench_fq_from_repr, -); diff --git a/pairing/benches/bls12_381/fq12.rs b/pairing/benches/bls12_381/fq12.rs deleted file mode 100644 index 2d6ed490e2..0000000000 --- a/pairing/benches/bls12_381/fq12.rs +++ /dev/null @@ -1,125 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use ff::Field; -use pairing::bls12_381::*; - -fn bench_fq12_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_squaring(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq12::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq12::invert", |b| { - b.iter(|| { - let tmp = v[count].invert(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_fq12_add_assign, - bench_fq12_sub_assign, - bench_fq12_mul_assign, - bench_fq12_squaring, - bench_fq12_invert, -); diff --git a/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs deleted file mode 100644 index 1402efa255..0000000000 --- a/pairing/benches/bls12_381/fq2.rs +++ /dev/null @@ -1,146 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use ff::Field; -use pairing::bls12_381::*; - -fn bench_fq2_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_squaring(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::invert", |b| { - b.iter(|| { - let tmp = v[count].invert(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::sqrt", |b| { - b.iter(|| { - let tmp = v[count].sqrt(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_fq2_add_assign, - bench_fq2_sub_assign, - bench_fq2_mul_assign, - bench_fq2_squaring, - bench_fq2_invert, - bench_fq2_sqrt, -); diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs deleted file mode 100644 index 468d68eb45..0000000000 --- a/pairing/benches/bls12_381/fr.rs +++ /dev/null @@ -1,209 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use ff::{Field, PrimeField}; -use pairing::bls12_381::*; - -fn bench_fr_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_square(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::invert", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].invert() - }) - }); -} - -fn bench_fr_neg(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::neg", |b| { - b.iter(|| { - let tmp = v[count].neg(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).square()) - .collect(); - - let mut count = 0; - c.bench_function("Fr::sqrt", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() - }) - }); -} - -fn bench_fr_to_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::to_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].to_repr() - }) - }); -} - -fn bench_fr_from_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).to_repr()) - .collect(); - - let mut count = 0; - c.bench_function("Fr::from_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - Fr::from_repr(v[count]) - }) - }); -} - -criterion_group!( - benches, - bench_fr_add_assign, - bench_fr_sub_assign, - bench_fr_mul_assign, - bench_fr_square, - bench_fr_invert, - bench_fr_neg, - bench_fr_sqrt, - bench_fr_to_repr, - bench_fr_from_repr, -); diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs deleted file mode 100644 index ed49daa93a..0000000000 --- a/pairing/benches/bls12_381/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -pub(crate) mod ec; -pub(crate) mod fq; -pub(crate) mod fq12; -pub(crate) mod fq2; -pub(crate) mod fr; - -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; - -use group::Group; -use pairing::bls12_381::*; -use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; - -fn bench_pairing_g2_preparation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("G2 preparation", |b| { - b.iter(|| { - let tmp = G2Prepared::from(G2Affine::from(v[count])); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_miller_loop(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(G1Affine, G2Prepared)> = (0..SAMPLES) - .map(|_| { - ( - G1Affine::from(G1::random(&mut rng)), - G2Affine::from(G2::random(&mut rng)).into(), - ) - }) - .collect(); - - let mut count = 0; - c.bench_function("Miller loop", |b| { - b.iter(|| { - let tmp = Bls12::multi_miller_loop(&[(&v[count].0, &v[count].1)]); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_final_exponentiation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| { - ( - G1Affine::from(G1::random(&mut rng)), - G2Affine::from(G2::random(&mut rng)).into(), - ) - }) - .map(|(ref p, ref q)| Bls12::multi_miller_loop(&[(p, q)])) - .collect(); - - let mut count = 0; - c.bench_function("Final exponentiation", |b| { - b.iter(|| { - let tmp = v[count].final_exponentiation(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_full(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(G1Affine, G2Affine)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng).into(), G2::random(&mut rng).into())) - .collect(); - - let mut count = 0; - c.bench_function("Full pairing", |b| { - b.iter(|| { - let tmp = Bls12::pairing(&v[count].0, &v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_pairing_g2_preparation, - bench_pairing_miller_loop, - bench_pairing_final_exponentiation, - bench_pairing_full, -); diff --git a/pairing/benches/pairing_benches.rs b/pairing/benches/pairing_benches.rs deleted file mode 100644 index 7abc824537..0000000000 --- a/pairing/benches/pairing_benches.rs +++ /dev/null @@ -1,12 +0,0 @@ -use criterion::criterion_main; -mod bls12_381; - -criterion_main!( - bls12_381::benches, - bls12_381::ec::g1::benches, - bls12_381::ec::g2::benches, - bls12_381::fq::benches, - bls12_381::fq12::benches, - bls12_381::fq2::benches, - bls12_381::fr::benches, -); diff --git a/pairing/src/bls12_381/README.md b/pairing/src/bls12_381/README.md deleted file mode 100644 index d3811a142e..0000000000 --- a/pairing/src/bls12_381/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# BLS12-381 - -This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction. - -## BLS12 Parameterization - -BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by: - -* q = (x - 1)2 ((x4 - x2 + 1) / 3) + x -* r = (x4 - x2 + 1) - -Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F*q* which contains a subgroup of order *r* such that *r* | (*q*12 - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field Fq2 gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of Fq12. - -In zk-SNARK schemes, we require Fr with large 2n roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2n | (r - 1), or equivalently that *x* has a large 2n factor, gives rise to BLS12 curves suitable for zk-SNARKs. - -Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions. - -Many curves match our descriptions, but we require some extra properties for efficiency purposes: - -* *q* should be smaller than 2383, and *r* should be smaller than 2255, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions. -* Fq12 is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms. -* We desire *x* of small Hamming weight, to increase the performance of the pairing function. - -## BLS12-381 Instantiation - -The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces: - -* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits) -* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits) - -Our extension field tower is constructed as follows: - -1. Fq2 is constructed as Fq(u) / (u2 - β) where β = -1. -2. Fq6 is constructed as Fq2(v) / (v3 - ξ) where ξ = u + 1 -3. Fq12 is constructed as Fq6(w) / (w2 - γ) where γ = v - -Now, we instantiate the elliptic curve E(Fq) : y2 = x3 + 4, and the elliptic curve E'(Fq2) : y2 = x3 + 4(u + 1). - -The group G1 is the *r* order subgroup of E, which has cofactor (x - 1)2 / 3. The group G2 is the *r* order subgroup of E', which has cofactor (x8 - 4x7 + 5x6 - 4x4 + 6x3 - 4x2 - 4x + 13) / 9. - -### Generators - -The generators of G1 and G2 are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity. - -#### G1 - -``` -x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 -y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -``` - -#### G2 - -``` -x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 -y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 -``` - -### Serialization - -* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form. -* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form. -* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates. -* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form. - -The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element: - -* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. -* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. -* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. - diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs deleted file mode 100644 index 5099307e4f..0000000000 --- a/pairing/src/bls12_381/ec.rs +++ /dev/null @@ -1,2199 +0,0 @@ -macro_rules! curve_impl { - ( - $name:expr, - $projective:ident, - $affine:ident, - $basefield:ident, - $scalarfield:ident, - $uncompressed:ident, - $compressed:ident, - $pairing:ident - ) => { - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - pub struct $affine { - pub(crate) x: $basefield, - pub(crate) y: $basefield, - pub(crate) infinity: bool, - } - - impl Default for $affine { - fn default() -> Self { - Self::identity() - } - } - - impl ::std::fmt::Display for $affine { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - if self.infinity { - write!(f, "{}(Infinity)", $name) - } else { - write!(f, "{}(x={}, y={})", $name, self.x, self.y) - } - } - } - - impl ::subtle::ConditionallySelectable for $affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $affine { - x: $basefield::conditional_select(&a.x, &b.x, choice), - y: $basefield::conditional_select(&a.y, &b.y, choice), - // Obviously not constant-time, but this code will be replaced. - infinity: if choice.into() { - b.infinity - } else { - a.infinity - }, - } - } - } - - #[derive(Copy, Clone, Debug, Eq)] - pub struct $projective { - pub(crate) x: $basefield, - pub(crate) y: $basefield, - pub(crate) z: $basefield, - } - - impl ::std::fmt::Display for $projective { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}", self.to_affine()) - } - } - - impl PartialEq for $projective { - fn eq(&self, other: &$projective) -> bool { - if self.is_identity().into() { - return other.is_identity().into(); - } - - if other.is_identity().into() { - return false; - } - - // The points (X, Y, Z) and (X', Y', Z') - // are equal when (X * Z^2) = (X' * Z'^2) - // and (Y * Z^3) = (Y' * Z'^3). - - let mut z1 = self.z.square(); - let mut z2 = other.z.square(); - - let mut tmp1 = self.x; - tmp1.mul_assign(&z2); - - let mut tmp2 = other.x; - tmp2.mul_assign(&z1); - - if tmp1 != tmp2 { - return false; - } - - z1.mul_assign(&self.z); - z2.mul_assign(&other.z); - z2.mul_assign(&self.y); - z1.mul_assign(&other.y); - - if z1 != z2 { - return false; - } - - true - } - } - - impl $affine { - fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { - let mut res = $projective::identity(); - for i in bits { - res = res.double(); - if i { - res.add_assign(self) - } - } - res - } - - fn mul_bits_u8>(&self, bits: BitIterator) -> $projective { - let mut res = $projective::identity(); - for i in bits { - res = res.double(); - if i { - res.add_assign(self) - } - } - res - } - - /// Attempts to construct an affine point given an x-coordinate. The - /// point is not guaranteed to be in the prime order subgroup. - /// - /// If and only if `greatest` is set will the lexicographically - /// largest y-coordinate be selected. - fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> { - // Compute x^3 + b - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&$affine::get_coeff_b()); - - x3b.sqrt().map(|y| { - let negy = y.neg(); - - $affine { - x: x, - y: if (y < negy) ^ greatest { y } else { negy }, - infinity: false, - } - }) - } - - fn is_on_curve(&self) -> bool { - if self.is_identity().into() { - true - } else { - // Check that the point is on the curve - let y2 = self.y.square(); - - let mut x3b = self.x.square(); - x3b.mul_assign(&self.x); - x3b.add_assign(&Self::get_coeff_b()); - - y2 == x3b - } - } - - fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { - let bits = BitIterator::::new($scalarfield::char()); - self.mul_bits_u8(bits).is_identity().into() - } - } - - impl ::std::ops::Neg for $affine { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - let mut ret = self; - if bool::from(!ret.is_identity()) { - ret.y = ret.y.neg(); - } - ret - } - } - - impl ::std::ops::Mul<$scalarfield> for $affine { - type Output = $projective; - - fn mul(self, by: $scalarfield) -> $projective { - let bits = BitIterator::::Repr>::new(by.into()); - self.mul_bits_u8(bits) - } - } - - impl<'r> ::std::ops::Mul<&'r $scalarfield> for $affine { - type Output = $projective; - - fn mul(self, by: &'r $scalarfield) -> $projective { - let bits = BitIterator::::Repr>::new(by.into()); - self.mul_bits_u8(bits) - } - } - - impl PrimeCurveAffine for $affine { - type Scalar = $scalarfield; - type Curve = $projective; - - fn identity() -> Self { - $affine { - x: $basefield::zero(), - y: $basefield::one(), - infinity: true, - } - } - - fn generator() -> Self { - Self::get_generator() - } - - fn is_identity(&self) -> Choice { - Choice::from(if self.infinity { 1 } else { 0 }) - } - - fn to_curve(&self) -> $projective { - (*self).into() - } - } - - impl GroupEncoding for $projective { - type Repr = $compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - if let Ok(affine) = bytes.into_affine_unchecked() { - // NB: Decompression guarantees that it is on the curve already. - CtOption::new( - affine.into(), - Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { - 1 - } else { - 0 - }), - ) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - if let Ok(p) = bytes.into_affine_unchecked() { - CtOption::new(p.into(), Choice::from(1)) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn to_bytes(&self) -> Self::Repr { - self.to_affine().to_bytes() - } - } - - impl GroupEncoding for $affine { - type Repr = $compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_bytes_unchecked(bytes).and_then(|affine| { - // NB: Decompression guarantees that it is on the curve already. - CtOption::new( - affine, - Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { - 1 - } else { - 0 - }), - ) - }) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - if let Ok(p) = bytes.into_affine_unchecked() { - CtOption::new(p, Choice::from(1)) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn to_bytes(&self) -> Self::Repr { - $compressed::from_affine(*self) - } - } - - impl UncompressedEncoding for $affine { - type Uncompressed = $uncompressed; - - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed_unchecked(bytes).and_then(|affine| { - CtOption::new( - affine, - Choice::from( - if affine.is_on_curve() - && affine.is_in_correct_subgroup_assuming_on_curve() - { - 1 - } else { - 0 - }, - ), - ) - }) - } - - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { - if let Ok(p) = bytes.into_affine_unchecked() { - CtOption::new(p, Choice::from(1)) - } else { - CtOption::new(Self::identity(), Choice::from(0)) - } - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - $uncompressed::from_affine(*self) - } - } - - impl PairingCurveAffine for $affine { - type Pair = $pairing; - type PairingResult = Gt; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - self.perform_pairing(other) - } - } - - impl ::std::iter::Sum for $projective { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), ::std::ops::Add::add) - } - } - - impl<'r> ::std::iter::Sum<&'r $projective> for $projective { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), ::std::ops::Add::add) - } - } - - impl ::std::ops::Neg for $projective { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - let mut ret = self; - if bool::from(!ret.is_identity()) { - ret.y = ret.y.neg(); - } - ret - } - } - - impl<'r> ::std::ops::Add<&'r $projective> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: &Self) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::std::ops::Add for $projective { - type Output = Self; - - #[inline] - fn add(self, other: Self) -> Self { - self + &other - } - } - - impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective { - fn add_assign(&mut self, other: &Self) { - if self.is_identity().into() { - *self = *other; - return; - } - - if other.is_identity().into() { - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl - - // Z1Z1 = Z1^2 - let z1z1 = self.z.square(); - - // Z2Z2 = Z2^2 - let z2z2 = other.z.square(); - - // U1 = X1*Z2Z2 - let mut u1 = self.x; - u1.mul_assign(&z2z2); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S1 = Y1*Z2*Z2Z2 - let mut s1 = self.y; - s1.mul_assign(&other.z); - s1.mul_assign(&z2z2); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if u1 == u2 && s1 == s2 { - // The two points are equal, so we double. - *self = self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-U1 - let mut h = u2; - h.sub_assign(&u1); - - // I = (2*H)^2 - let i = h.double().square(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-S1) - let mut r = s2; - r.sub_assign(&s1); - r = r.double(); - - // V = U1*I - let mut v = u1; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V - X3) - 2*S1*J - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - s1.mul_assign(&j); // S1 = S1 * J * 2 - s1 = s1.double(); - self.y.sub_assign(&s1); - - // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H - self.z.add_assign(&other.z); - self.z = self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&z2z2); - self.z.mul_assign(&h); - } - } - } - - impl ::std::ops::AddAssign for $projective { - #[inline] - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } - } - - impl<'r> ::std::ops::Sub<&'r $projective> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: &Self) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::std::ops::Sub for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: Self) -> Self { - self - &other - } - } - - impl<'r> ::std::ops::SubAssign<&'r $projective> for $projective { - fn sub_assign(&mut self, other: &Self) { - self.add_assign(&other.neg()); - } - } - - impl ::std::ops::SubAssign for $projective { - #[inline] - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } - } - - impl<'r> ::std::ops::Add<&'r $affine> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: &$affine) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::std::ops::Add<$affine> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: $affine) -> Self { - self + &other - } - } - - impl<'r> ::std::ops::AddAssign<&'r $affine> for $projective { - fn add_assign(&mut self, other: &$affine) { - if other.is_identity().into() { - return; - } - - if self.is_identity().into() { - self.x = other.x; - self.y = other.y; - self.z = $basefield::one(); - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl - - // Z1Z1 = Z1^2 - let z1z1 = self.z.square(); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if self.x == u2 && self.y == s2 { - // The two points are equal, so we double. - *self = self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-X1 - let mut h = u2; - h.sub_assign(&self.x); - - // HH = H^2 - let hh = h.square(); - - // I = 4*HH - let i = hh.double().double(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-Y1) - let mut r = s2; - r.sub_assign(&self.y); - r = r.double(); - - // V = X1*I - let mut v = self.x; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V-X3)-2*Y1*J - j.mul_assign(&self.y); // J = 2*Y1*J - j = j.double(); - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - self.y.sub_assign(&j); - - // Z3 = (Z1+H)^2-Z1Z1-HH - self.z.add_assign(&h); - self.z = self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&hh); - } - } - } - - impl ::std::ops::AddAssign<$affine> for $projective { - #[inline] - fn add_assign(&mut self, other: $affine) { - self.add_assign(&other); - } - } - - impl<'r> ::std::ops::Sub<&'r $affine> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: &$affine) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::std::ops::Sub<$affine> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: $affine) -> Self { - self - &other - } - } - - impl<'r> ::std::ops::SubAssign<&'r $affine> for $projective { - fn sub_assign(&mut self, other: &$affine) { - self.add_assign(&other.neg()); - } - } - - impl ::std::ops::SubAssign<$affine> for $projective { - #[inline] - fn sub_assign(&mut self, other: $affine) { - self.sub_assign(&other); - } - } - - impl ::std::ops::Mul<<$projective as Group>::Scalar> for $projective { - type Output = Self; - - fn mul(mut self, other: <$projective as Group>::Scalar) -> Self { - self.mul_assign(&other); - self - } - } - - impl<'r> ::std::ops::Mul<&'r <$projective as Group>::Scalar> for $projective { - type Output = Self; - - fn mul(mut self, other: &'r <$projective as Group>::Scalar) -> Self { - self.mul_assign(other); - self - } - } - - impl ::std::ops::MulAssign<<$projective as Group>::Scalar> for $projective { - fn mul_assign(&mut self, other: <$projective as Group>::Scalar) { - self.mul_assign(&other); - } - } - - impl<'r> ::std::ops::MulAssign<&'r <$projective as Group>::Scalar> for $projective { - fn mul_assign(&mut self, other: &'r <$projective as Group>::Scalar) { - let mut res = Self::identity(); - - let mut found_one = false; - - for i in BitIterator::::new(other.to_repr()) { - if found_one { - res = res.double(); - } else { - found_one = i; - } - - if i { - res.add_assign(&*self); - } - } - - *self = res; - } - } - - impl Group for $projective { - type Scalar = $scalarfield; - - fn random(rng: &mut R) -> Self { - loop { - let x = $basefield::random(rng); - let greatest = rng.next_u32() % 2 != 0; - - let p = $affine::get_point_from_x(x, greatest); - if p.is_some().into() { - let p = p.unwrap().scale_by_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - // The point at infinity is always represented by - // Z = 0. - fn identity() -> Self { - $projective { - x: $basefield::zero(), - y: $basefield::one(), - z: $basefield::zero(), - } - } - - fn generator() -> Self { - $affine::generator().into() - } - - // The point at infinity is always represented by - // Z = 0. - fn is_identity(&self) -> Choice { - Choice::from(if self.z.is_zero() { 1 } else { 0 }) - } - - fn double(&self) -> Self { - if self.is_identity().into() { - return *self; - } - - // Other than the point at infinity, no points on E or E' - // can double to equal the point at infinity, as y=0 is - // never true for points on the curve. (-4 and -4u-4 - // are not cubic residue in their respective fields.) - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - - // A = X1^2 - let a = self.x.square(); - - // B = Y1^2 - let b = self.y.square(); - - // C = B^2 - let c = b.square(); - - // D = 2*((X1+B)2-A-C) - let mut d = self.x; - d.add_assign(&b); - d = d.square(); - d.sub_assign(&a); - d.sub_assign(&c); - d = d.double(); - - // E = 3*A - let mut e = a.double(); - e.add_assign(&a); - - // F = E^2 - let f = e.square(); - - // Z3 = 2*Y1*Z1 - let z = self.z.double() * self.y; - - // X3 = F-2*D - let x = f - d.double(); - - // Y3 = E*(D-X3)-8*C - let y = e * (d - x) - c.double().double().double(); - - $projective { x, y, z } - } - } - - impl PrimeGroup for $projective {} - - impl Curve for $projective { - type AffineRepr = $affine; - - fn batch_normalize(p: &[Self], q: &mut [$affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = $basefield::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of $affine to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - if bool::from(!p.is_identity()) { - acc *= p.z; - } - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - if bool::from(!skip) { - acc *= p.z; - } - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - if skip.into() { - *q = $affine::identity(); - } else { - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = false; - } - } - } - - fn to_affine(&self) -> $affine { - (*self).into() - } - } - - impl WnafGroup for $projective { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) - } - } - - impl PrimeCurve for $projective { - type Affine = $affine; - } - - // The affine point X, Y is represented in the jacobian - // coordinates with Z = 1. - impl From<$affine> for $projective { - fn from(p: $affine) -> $projective { - if p.is_identity().into() { - $projective::identity() - } else { - $projective { - x: p.x, - y: p.y, - z: $basefield::one(), - } - } - } - } - - // The projective point X, Y, Z is represented in the affine - // coordinates as X/Z^2, Y/Z^3. - impl From<$projective> for $affine { - fn from(p: $projective) -> $affine { - if p.is_identity().into() { - $affine::identity() - } else if p.z == $basefield::one() { - // If Z is one, the point is already normalized. - $affine { - x: p.x, - y: p.y, - infinity: false, - } - } else { - // Z is nonzero, so it must have an inverse in a field. - let zinv = p.z.invert().unwrap(); - let mut zinv_powered = zinv.square(); - - // X/Z^2 - let mut x = p.x; - x.mul_assign(&zinv_powered); - - // Y/Z^3 - let mut y = p.y; - zinv_powered.mul_assign(&zinv); - y.mul_assign(&zinv_powered); - - $affine { - x: x, - y: y, - infinity: false, - } - } - } - } - }; -} - -use std::error::Error; -use std::fmt; - -/// An error that may occur when trying to decode an `EncodedPoint`. -#[derive(Debug)] -enum GroupDecodingError { - /// The coordinate(s) do not lie on the curve. - NotOnCurve, - /// One of the coordinates could not be decoded - CoordinateDecodingError(&'static str), - /// The compression mode of the encoded element was not as expected - UnexpectedCompressionMode, - /// The encoding contained bits that should not have been set - UnexpectedInformation, -} - -impl Error for GroupDecodingError { - fn description(&self) -> &str { - match *self { - GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve", - GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded", - GroupDecodingError::UnexpectedCompressionMode => { - "encoding has unexpected compression mode" - } - GroupDecodingError::UnexpectedInformation => "encoding has unexpected information", - } - } -} - -impl fmt::Display for GroupDecodingError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - GroupDecodingError::CoordinateDecodingError(description) => { - write!(f, "{} decoding error", description) - } - _ => write!(f, "{}", self), - } - } -} - -pub mod g1 { - use super::super::{Fq, FqRepr, Fr, Gt}; - use super::{g2::G2Affine, GroupDecodingError}; - use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField}; - use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, - }; - use rand_core::RngCore; - use std::fmt; - use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::{Choice, CtOption}; - - curve_impl!( - "G1", - G1, - G1Affine, - Fq, - Fr, - G1Uncompressed, - G1Compressed, - G2Affine - ); - - #[derive(Copy, Clone)] - pub struct G1Uncompressed([u8; 96]); - - impl Default for G1Uncompressed { - fn default() -> Self { - G1Uncompressed([0; 96]) - } - } - - impl AsRef<[u8]> for G1Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G1Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl G1Uncompressed { - #[cfg(test)] - pub(crate) fn size() -> usize { - 96 - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) != 0 { - // Distinguisher bit is set, but this should be uncompressed! - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::identity()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - if copy[0] & (1 << 5) != 0 { - // The bit indicating the y-coordinate should be lexicographically - // largest is set, but this is an uncompressed element. - return Err(GroupDecodingError::UnexpectedInformation); - } - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { - let mut ret = [0; 48]; - ret.copy_from_slice(&s[start..start + 48]); - ret - } - - let x = FqRepr(copy_segment(©, 0)); - let y = FqRepr(copy_segment(©, 48)); - - Ok(G1Affine { - x: Fq::from_repr(x).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("x coordinate") - })?, - y: Fq::from_repr(y).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("y coordinate") - })?, - infinity: false, - }) - } - } - fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::default(); - - if affine.is_identity().into() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - res.0[..48].copy_from_slice(&affine.x.to_repr().0); - res.0[48..].copy_from_slice(&affine.y.to_repr().0); - } - - res - } - } - - #[derive(Copy, Clone)] - pub struct G1Compressed([u8; 48]); - - impl Default for G1Compressed { - fn default() -> Self { - G1Compressed([0; 48]) - } - } - - impl AsRef<[u8]> for G1Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G1Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl G1Compressed { - #[cfg(test)] - pub(crate) fn size() -> usize { - 48 - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) == 0 { - // Distinguisher bit isn't set. - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::identity()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - // Determine if the intended y coordinate must be greater - // lexicographically. - let greatest = copy[0] & (1 << 5) != 0; - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - // Interpret as Fq element. - let x = Fq::from_repr(FqRepr(copy)) - .ok_or_else(|| GroupDecodingError::CoordinateDecodingError("x coordinate"))?; - - let ret = G1Affine::get_point_from_x(x, greatest); - if ret.is_some().into() { - Ok(ret.unwrap()) - } else { - Err(GroupDecodingError::NotOnCurve) - } - } - } - fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::default(); - - if affine.is_identity().into() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - res.0 = affine.x.to_repr().0; - - let negy = affine.y.neg(); - - // Set the third most significant bit if the correct y-coordinate - // is lexicographically largest. - if affine.y > negy { - res.0[0] |= 1 << 5; - } - } - - // Set highest bit to distinguish this as a compressed element. - res.0[0] |= 1 << 7; - - res - } - } - - impl G1Affine { - fn scale_by_cofactor(&self) -> G1 { - // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 - let cofactor = BitIterator::::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); - self.mul_bits_u64(cofactor) - } - - fn get_generator() -> Self { - G1Affine { - x: super::super::fq::G1_GENERATOR_X, - y: super::super::fq::G1_GENERATOR_Y, - infinity: false, - } - } - - fn get_coeff_b() -> Fq { - super::super::fq::B_COEFF - } - - fn perform_pairing(&self, other: &G2Affine) -> Gt { - super::super::Bls12::pairing(self, other) - } - } - - impl G1 { - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } - } - - #[test] - fn g1_generator() { - let mut x = Fq::zero(); - let mut i = 0; - loop { - // y^2 = x^3 + b - let mut rhs = x.square(); - rhs.mul_assign(&x); - rhs.add_assign(&G1Affine::get_coeff_b()); - - let y = rhs.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - let negy = y.neg(); - - let p = G1Affine { - x, - y: if y < negy { y } else { negy }, - infinity: false, - }; - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - - let g1 = p.scale_by_cofactor(); - if bool::from(!g1.is_identity()) { - assert_eq!(i, 4); - let g1 = G1Affine::from(g1); - - assert!(g1.is_in_correct_subgroup_assuming_on_curve()); - - assert_eq!(g1, G1Affine::generator()); - break; - } - } - - i += 1; - x.add_assign(&Fq::one()); - } - } - - #[test] - fn g1_test_is_valid() { - // Reject point on isomorphic twist (b = 24) - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x0c, 0x3a, 0xd2, 0xae, 0xfd, 0xe0, 0xbb, 0x13, 0xf5, 0x83, 0xcc, 0x5a, 0x50, - 0x8f, 0x6a, 0x40, 0x9f, 0xe8, 0x3b, 0x1b, 0x4a, 0x5d, 0x64, 0x8d, 0xaf, 0x23, - 0xe0, 0x64, 0xf1, 0x13, 0x1e, 0xe5, 0x10, 0xcb, 0xfd, 0x30, 0x1d, 0x55, 0x38, - 0x22, 0xc5, 0x8d, 0x88, 0x7b, 0x66, 0xc0, 0x35, 0xdc, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x0f, 0xe3, 0xae, 0x09, 0x22, 0xdf, 0x70, 0x2c, 0x95, 0x37, 0x03, 0xf5, 0x79, - 0x5a, 0x39, 0xe5, 0xe7, 0x60, 0xf5, 0x79, 0x22, 0x99, 0x8c, 0x9d, 0x8a, 0xf1, - 0xcd, 0xb8, 0xaa, 0x8c, 0xe1, 0x67, 0xec, 0xd0, 0x1d, 0x51, 0x81, 0x30, 0x0d, - 0x35, 0x60, 0xaa, 0x6f, 0x95, 0x52, 0xf0, 0x3a, 0xae, - ])) - .unwrap(), - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point on a twist (b = 3) - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x0e, 0x45, 0xc9, 0xf0, 0xc0, 0x43, 0x86, 0x75, 0xbd, 0x88, 0x33, 0xdc, 0x7c, - 0x79, 0xa7, 0xf7, 0xea, 0x03, 0x4e, 0xe2, 0x92, 0x8b, 0x30, 0xa8, 0xe3, 0x05, - 0xbd, 0x1a, 0xc6, 0x5a, 0xdb, 0xa7, 0x92, 0xdd, 0xd3, 0x28, 0xf2, 0x7a, 0x4b, - 0xa6, 0xee, 0x6a, 0xdf, 0x83, 0x51, 0x1e, 0x15, 0xf5, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x11, 0x8d, 0x2c, 0x54, 0x3f, 0x03, 0x11, 0x02, 0x53, 0x2d, 0x0b, 0x64, 0x0b, - 0xd3, 0xff, 0x8b, 0x75, 0x3d, 0xdf, 0x21, 0xa2, 0x60, 0x1d, 0x20, 0xaa, 0x54, - 0x86, 0x82, 0xb2, 0x17, 0x26, 0xe5, 0xa6, 0x5c, 0xb8, 0x1e, 0x97, 0x5e, 0x86, - 0x75, 0x3b, 0x45, 0x0e, 0xb1, 0xab, 0x7b, 0x5d, 0xad, - ])) - .unwrap(), - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point in an invalid subgroup - // There is only one r-order subgroup, as r does not divide the cofactor. - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x12, 0xa8, 0x77, 0x80, 0x88, 0x45, 0x83, 0x08, 0x26, 0x5b, 0xdd, 0xd2, 0x3d, - 0x1d, 0xec, 0x54, 0xf3, 0x5d, 0xe9, 0xce, 0x0d, 0x6b, 0x4e, 0x84, 0x88, 0xae, - 0x9c, 0x49, 0x9f, 0x46, 0xf0, 0xc0, 0xe3, 0x7e, 0x1a, 0x61, 0x0e, 0xff, 0x2f, - 0x79, 0x76, 0xe1, 0xc9, 0x71, 0xc6, 0xdb, 0x8f, 0xe8, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x0e, 0xd7, 0x4a, 0xb9, 0xf3, 0x02, 0xcb, 0xe0, 0x5b, 0x6f, 0xda, 0x44, 0xad, - 0x85, 0xfa, 0x78, 0x92, 0x1b, 0xee, 0xf8, 0x9d, 0x4f, 0x29, 0xdf, 0x1b, 0xa1, - 0x94, 0xe8, 0x9b, 0xab, 0x26, 0x10, 0xc5, 0x7c, 0xa5, 0x54, 0x56, 0xfc, 0xb9, - 0xae, 0x8a, 0x22, 0xde, 0xfa, 0x0d, 0x52, 0x62, 0x56, - ])) - .unwrap(), - infinity: false, - }; - assert!(p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - } - - #[test] - fn test_g1_addition_correctness() { - let mut p = G1 { - x: Fq::from_repr(FqRepr([ - 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, - 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, - 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, - 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, - 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, - 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, - 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, - ])) - .unwrap(), - z: Fq::one(), - }; - - p.add_assign(&G1 { - x: Fq::from_repr(FqRepr([ - 0x08, 0xab, 0xd6, 0x23, 0xa5, 0x94, 0xfb, 0xa8, 0x24, 0xe8, 0x53, 0x87, 0x37, 0xc6, - 0xe6, 0x75, 0x5f, 0x44, 0x31, 0x4e, 0xc5, 0xe3, 0xfb, 0x03, 0xc2, 0x86, 0xc0, 0x21, - 0x1c, 0x40, 0xdd, 0x54, 0xa1, 0x2b, 0xeb, 0x1f, 0xea, 0x10, 0x56, 0xe6, 0xee, 0xc7, - 0x8f, 0x30, 0x96, 0x21, 0x3c, 0xbf, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x00, 0xe7, 0x48, 0x46, 0x15, 0x4a, 0x9e, 0x44, 0x1f, 0x29, 0x98, 0xa5, 0xa9, 0xc6, - 0x12, 0x53, 0xd6, 0x51, 0x04, 0xc6, 0xf9, 0x5a, 0x87, 0x2a, 0x9a, 0x51, 0x81, 0xf2, - 0xfa, 0xc2, 0x26, 0xad, 0x2f, 0xde, 0xb5, 0xc8, 0x29, 0x17, 0xff, 0x9e, 0x6b, 0x05, - 0x28, 0xf0, 0x88, 0xbb, 0x70, 0x44, - ])) - .unwrap(), - z: Fq::one(), - }); - - let p = G1Affine::from(p); - - assert_eq!( - p, - G1Affine { - x: Fq::from_repr(FqRepr([ - 0x17, 0xfb, 0x89, 0x05, 0xe9, 0x18, 0x3c, 0x69, 0xd1, 0x78, 0xb2, 0x8d, 0xd4, - 0xf4, 0x07, 0xef, 0xc4, 0xf9, 0xa5, 0x2a, 0x42, 0x8e, 0x23, 0xbb, 0xeb, 0x96, - 0xbb, 0x99, 0xfa, 0x50, 0x77, 0x9f, 0xe8, 0x65, 0xd2, 0x21, 0xc8, 0x09, 0x02, - 0x60, 0x06, 0xdd, 0x30, 0x98, 0xf2, 0x22, 0x35, 0xdf, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x04, 0x30, 0xcb, 0xdc, 0x54, 0x55, 0xb0, 0x0a, 0x4b, 0xc3, 0x62, 0x64, 0x9d, - 0xce, 0x63, 0x76, 0xee, 0xc8, 0xd1, 0xa5, 0xb7, 0x46, 0x6c, 0x58, 0x10, 0x40, - 0xe2, 0x70, 0x12, 0xf2, 0x0b, 0x64, 0xf6, 0xa0, 0x5f, 0x2b, 0xcf, 0x1d, 0x9c, - 0xa7, 0xd0, 0xde, 0x9d, 0x65, 0x29, 0x2b, 0x77, 0x10, - ])) - .unwrap(), - infinity: false, - } - ); - } - - #[test] - fn test_g1_doubling_correctness() { - let p = G1 { - x: Fq::from_repr(FqRepr([ - 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, - 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, - 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, - 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, - 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, - 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, - 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, - ])) - .unwrap(), - z: Fq::one(), - }; - - let p = G1Affine::from(p.double()); - - assert_eq!( - p, - G1Affine { - x: Fq::from_repr(FqRepr([ - 0x0a, 0xf9, 0x60, 0xcf, 0xf3, 0xd8, 0x38, 0x33, 0x66, 0xc8, 0xba, 0xf1, 0x77, - 0xd2, 0x05, 0x33, 0x4b, 0x91, 0x4c, 0x16, 0x68, 0x7d, 0xcd, 0xe0, 0xce, 0x0e, - 0x9c, 0x38, 0xfd, 0xb1, 0x18, 0x51, 0x03, 0xb0, 0x39, 0x42, 0xe7, 0x32, 0xae, - 0xcb, 0xf9, 0x39, 0xdd, 0xfe, 0x0e, 0xad, 0x70, 0x18, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x13, 0x50, 0x75, 0x58, 0x9a, 0x68, 0x7b, 0x1e, 0x8b, 0x54, 0x7c, 0x13, 0x13, - 0xb2, 0x75, 0x55, 0x17, 0x71, 0xa6, 0x5b, 0x60, 0x57, 0x2f, 0x4e, 0x90, 0x96, - 0x38, 0x0d, 0xd8, 0xe5, 0x1b, 0x11, 0x2b, 0x6d, 0x82, 0xae, 0x17, 0x8a, 0x1b, - 0xa0, 0x3f, 0x06, 0x75, 0x69, 0x5f, 0x51, 0x77, 0xa8, - ])) - .unwrap(), - infinity: false, - } - ); - } - - #[test] - fn test_g1_same_y() { - // Test the addition of two points with different x coordinates - // but the same y coordinate. - - // x1 = 128100205326445210408953809171070606737678357140298133325128175840781723996595026100005714405541449960643523234125 - // x2 = 3821408151224848222394078037104966877485040835569514006839342061575586899845797797516352881516922679872117658572470 - // y = 2291134451313223670499022936083127939567618746216464377735567679979105510603740918204953301371880765657042046687078 - - let a = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x00, 0xd5, 0x10, 0x8d, 0x8f, 0xf1, 0xfb, 0xd6, 0x07, 0x41, 0x8d, 0x48, 0x43, 0x86, - 0xd2, 0x67, 0x07, 0x1f, 0xfa, 0x80, 0x21, 0x53, 0x17, 0x05, 0xfe, 0x66, 0x9f, 0x13, - 0x3f, 0x16, 0xc2, 0x6a, 0x3a, 0xd2, 0x35, 0x4a, 0x07, 0xf5, 0x47, 0x2b, 0xea, 0x43, - 0x1f, 0x2c, 0xc3, 0x8f, 0xc9, 0x4d, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, - 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, - 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, - 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, - ])) - .unwrap(), - infinity: false, - }; - - let b = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x18, 0xd4, 0x04, 0x3e, 0x78, 0x10, 0x31, 0x06, 0xf7, 0xc7, 0x59, 0x10, 0x81, 0x6f, - 0x20, 0x7c, 0xc6, 0xe0, 0x52, 0x01, 0xe5, 0xf8, 0x39, 0x91, 0xe7, 0x02, 0xf1, 0x4b, - 0xb0, 0xe2, 0xac, 0xa5, 0xd9, 0x04, 0x0b, 0x2d, 0x75, 0x44, 0x8a, 0xd9, 0xe0, 0x6c, - 0xdb, 0x15, 0x6b, 0x63, 0x56, 0xb6, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, - 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, - 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, - 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, - ])) - .unwrap(), - infinity: false, - }; - - // Expected - // x = 52901198670373960614757979459866672334163627229195745167587898707663026648445040826329033206551534205133090753192 - // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 - let c = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x00, 0x57, 0xfd, 0x1e, 0x31, 0x7d, 0xb9, 0xbd, 0x4c, 0x12, 0xc1, 0x5d, 0x7e, 0x55, - 0xb9, 0xf3, 0x96, 0x76, 0xff, 0x02, 0xec, 0x39, 0xc2, 0x27, 0x81, 0xc7, 0x42, 0x42, - 0x06, 0xb7, 0x87, 0x14, 0x0a, 0xd5, 0xbf, 0x87, 0x34, 0x1a, 0x2d, 0xf9, 0xef, 0x4f, - 0x05, 0xbd, 0xd1, 0x0c, 0x8a, 0xa8, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x0b, 0x1e, 0x4e, 0x11, 0x17, 0x7f, 0x59, 0xd4, 0x46, 0x96, 0xde, 0xb9, 0xab, 0x2b, - 0xa3, 0xe7, 0x12, 0x67, 0xd7, 0x0d, 0xb5, 0x10, 0x49, 0xfb, 0xa6, 0x99, 0x8d, 0xba, - 0xa6, 0x00, 0xf1, 0x8a, 0xf9, 0x55, 0xcd, 0x68, 0x61, 0x5f, 0xf0, 0xb5, 0x12, 0x88, - 0x33, 0x40, 0x16, 0x67, 0x93, 0x45, - ])) - .unwrap(), - infinity: false, - }; - - assert!(a.is_on_curve() && a.is_in_correct_subgroup_assuming_on_curve()); - assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); - assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); - - let mut tmp1 = a.to_curve(); - tmp1.add_assign(&b.to_curve()); - assert_eq!(tmp1.to_affine(), c); - assert_eq!(tmp1, c.to_curve()); - - let mut tmp2 = a.to_curve(); - tmp2.add_assign(&b); - assert_eq!(tmp2.to_affine(), c); - assert_eq!(tmp2, c.to_curve()); - } - - #[test] - fn g1_curve_tests() { - use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests}; - curve_tests::(); - random_wnaf_tests::(); - random_uncompressed_encoding_tests::(); - } -} - -pub mod g2 { - use super::super::{Fq, Fq2, FqRepr, Fr, Gt}; - use super::{g1::G1Affine, GroupDecodingError}; - use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField}; - use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, - }; - use rand_core::RngCore; - use std::fmt; - use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::{Choice, CtOption}; - - curve_impl!( - "G2", - G2, - G2Affine, - Fq2, - Fr, - G2Uncompressed, - G2Compressed, - G1Affine - ); - - #[derive(Copy, Clone)] - pub struct G2Uncompressed([u8; 192]); - - impl Default for G2Uncompressed { - fn default() -> Self { - G2Uncompressed([0; 192]) - } - } - - impl AsRef<[u8]> for G2Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G2Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl G2Uncompressed { - #[cfg(test)] - pub(crate) fn size() -> usize { - 192 - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) != 0 { - // Distinguisher bit is set, but this should be uncompressed! - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::identity()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - if copy[0] & (1 << 5) != 0 { - // The bit indicating the y-coordinate should be lexicographically - // largest is set, but this is an uncompressed element. - return Err(GroupDecodingError::UnexpectedInformation); - } - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { - let mut ret = [0; 48]; - ret.copy_from_slice(&s[start..start + 48]); - ret - } - - let x_c1 = FqRepr(copy_segment(©, 0)); - let x_c0 = FqRepr(copy_segment(©, 48)); - let y_c1 = FqRepr(copy_segment(©, 96)); - let y_c0 = FqRepr(copy_segment(©, 144)); - - Ok(G2Affine { - x: Fq2 { - c0: Fq::from_repr(x_c0).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") - })?, - c1: Fq::from_repr(x_c1).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") - })?, - }, - y: Fq2 { - c0: Fq::from_repr(y_c0).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c0)") - })?, - c1: Fq::from_repr(y_c1).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c1)") - })?, - }, - infinity: false, - }) - } - } - fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::default(); - - if affine.is_identity().into() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - res.0[0..48].copy_from_slice(&affine.x.c1.to_repr().0); - res.0[48..96].copy_from_slice(&affine.x.c0.to_repr().0); - res.0[96..144].copy_from_slice(&affine.y.c1.to_repr().0); - res.0[144..192].copy_from_slice(&affine.y.c0.to_repr().0); - } - - res - } - } - - #[derive(Copy, Clone)] - pub struct G2Compressed([u8; 96]); - - impl Default for G2Compressed { - fn default() -> Self { - G2Compressed([0; 96]) - } - } - - impl AsRef<[u8]> for G2Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G2Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl G2Compressed { - #[cfg(test)] - pub(crate) fn size() -> usize { - 96 - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) == 0 { - // Distinguisher bit isn't set. - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::identity()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - // Determine if the intended y coordinate must be greater - // lexicographically. - let greatest = copy[0] & (1 << 5) != 0; - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { - let mut ret = [0; 48]; - ret.copy_from_slice(&s[start..start + 48]); - ret - } - - let x_c1 = FqRepr(copy_segment(©, 0)); - let x_c0 = FqRepr(copy_segment(©, 48)); - - // Interpret as Fq element. - let x = Fq2 { - c0: Fq::from_repr(x_c0).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") - })?, - c1: Fq::from_repr(x_c1).ok_or_else(|| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") - })?, - }; - - let ret = G2Affine::get_point_from_x(x, greatest); - if ret.is_some().into() { - Ok(ret.unwrap()) - } else { - Err(GroupDecodingError::NotOnCurve) - } - } - } - fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::default(); - - if affine.is_identity().into() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - res.0[..48].copy_from_slice(&affine.x.c1.to_repr().0); - res.0[48..].copy_from_slice(&affine.x.c0.to_repr().0); - - let negy = affine.y.neg(); - - // Set the third most significant bit if the correct y-coordinate - // is lexicographically largest. - if affine.y > negy { - res.0[0] |= 1 << 5; - } - } - - // Set highest bit to distinguish this as a compressed element. - res.0[0] |= 1 << 7; - - res - } - } - - impl G2Affine { - fn get_generator() -> Self { - G2Affine { - x: Fq2 { - c0: super::super::fq::G2_GENERATOR_X_C0, - c1: super::super::fq::G2_GENERATOR_X_C1, - }, - y: Fq2 { - c0: super::super::fq::G2_GENERATOR_Y_C0, - c1: super::super::fq::G2_GENERATOR_Y_C1, - }, - infinity: false, - } - } - - fn get_coeff_b() -> Fq2 { - Fq2 { - c0: super::super::fq::B_COEFF, - c1: super::super::fq::B_COEFF, - } - } - - fn scale_by_cofactor(&self) -> G2 { - // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 - // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 - let cofactor = BitIterator::::new([ - 0xcf1c38e31c7238e5, - 0x1616ec6e786f0c70, - 0x21537e293a6691ae, - 0xa628f1cb4d9e82ef, - 0xa68a205b2e5a7ddf, - 0xcd91de4547085aba, - 0x91d50792876a202, - 0x5d543a95414e7f1, - ]); - self.mul_bits_u64(cofactor) - } - - fn perform_pairing(&self, other: &G1Affine) -> Gt { - super::super::Bls12::pairing(other, self) - } - } - - impl G2 { - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 11] = - [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } - } - - #[derive(Clone, Debug)] - pub struct G2Prepared { - pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, - pub(crate) infinity: bool, - } - - impl From for G2Prepared { - fn from(val: G2Affine) -> Self { - Self::from_affine(val) - } - } - - #[test] - fn g2_generator() { - let mut x = Fq2::zero(); - let mut i = 0; - loop { - // y^2 = x^3 + b - let mut rhs = x.square(); - rhs.mul_assign(&x); - rhs.add_assign(&G2Affine::get_coeff_b()); - - let y = rhs.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - let negy = y.neg(); - - let p = G2Affine { - x, - y: if y < negy { y } else { negy }, - infinity: false, - }; - - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - - let g2 = p.scale_by_cofactor(); - if bool::from(!g2.is_identity()) { - assert_eq!(i, 2); - let g2 = G2Affine::from(g2); - - assert!(g2.is_in_correct_subgroup_assuming_on_curve()); - assert_eq!(g2, G2Affine::generator()); - break; - } - } - - i += 1; - x.add_assign(&Fq2::one()); - } - } - - #[test] - fn g2_test_is_valid() { - // Reject point on isomorphic twist (b = 3 * (u + 1)) - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x10, 0xb8, 0xc0, 0x3d, 0x64, 0xdb, 0x4d, 0x0c, 0xcc, 0x65, 0x40, 0x6a, - 0x7c, 0x2e, 0x5a, 0x73, 0x7a, 0x17, 0xa0, 0x04, 0x74, 0x7e, 0x3d, 0xbe, - 0xc1, 0x59, 0x8e, 0xc4, 0x6f, 0xaa, 0x0c, 0x7c, 0xae, 0x3f, 0xb2, 0xfb, - 0x41, 0x8f, 0x6e, 0x8a, 0xa7, 0x57, 0x07, 0x2d, 0x9f, 0xa3, 0x5b, 0xa9, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x13, 0x42, 0xf0, 0x2e, 0x2d, 0xa5, 0x44, 0x05, 0x78, 0x9b, 0xac, 0x1f, - 0xec, 0x71, 0xa2, 0xb9, 0xfb, 0x77, 0x7e, 0x5b, 0x9b, 0x56, 0x86, 0x08, - 0x5b, 0x47, 0xa9, 0xff, 0x9a, 0x23, 0x3a, 0x50, 0xda, 0x30, 0x77, 0x2d, - 0xf0, 0xf5, 0x21, 0x2e, 0xd3, 0x0e, 0x70, 0xfe, 0x2f, 0x02, 0x97, 0x78, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0x40, 0xa0, 0x05, 0x45, 0xbb, 0x3c, 0x1e, 0x78, 0xe8, 0x2a, 0x79, - 0xd8, 0x29, 0xa5, 0x44, 0x66, 0x30, 0x15, 0xd9, 0x41, 0x0e, 0xb6, 0x08, - 0xa4, 0x93, 0xf3, 0x6b, 0xc2, 0x0b, 0xe9, 0x8a, 0xe4, 0x55, 0x17, 0x1a, - 0x3d, 0x47, 0xa6, 0x46, 0xfe, 0x08, 0x12, 0x04, 0x3d, 0xe5, 0x4d, 0xca, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x16, 0xa6, 0x13, 0xdb, 0x5c, 0x87, 0x3a, 0xaa, 0x68, 0x12, 0x8f, 0xd0, - 0x54, 0x8a, 0x38, 0x29, 0x15, 0x00, 0x8b, 0x1d, 0xc3, 0x99, 0xe8, 0xdf, - 0xda, 0x36, 0x1c, 0x97, 0xd0, 0x2f, 0x42, 0xb2, 0xb5, 0xac, 0x4d, 0xc9, - 0x20, 0x4b, 0xcf, 0xbd, 0x47, 0x09, 0x80, 0x23, 0x48, 0xe7, 0x93, 0x77, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point on a twist (b = 2 * (u + 1)) - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x06, 0x99, 0x3e, 0xc0, 0x1b, 0x89, 0x34, 0xed, 0xff, 0xcc, 0x4b, 0x2b, - 0x62, 0xce, 0x84, 0x84, 0x41, 0xab, 0xba, 0x71, 0x0d, 0x6c, 0x69, 0x2c, - 0x37, 0xc6, 0xb1, 0x2c, 0xca, 0x35, 0xa3, 0x4b, 0xc2, 0x91, 0x4d, 0xf6, - 0x88, 0x23, 0x32, 0x38, 0xf4, 0xfd, 0xfe, 0x95, 0xa7, 0x05, 0xf9, 0x17, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x10, 0xcf, 0x1d, 0x3a, 0xd8, 0xd9, 0x0b, 0xfa, 0x83, 0x80, 0x09, 0x65, - 0x82, 0x23, 0x67, 0xe7, 0xa5, 0xa0, 0xc2, 0xb7, 0x13, 0x1f, 0x35, 0x55, - 0xe9, 0x39, 0x46, 0xb2, 0x90, 0xca, 0xa5, 0x91, 0x44, 0x51, 0x64, 0x08, - 0xbc, 0x11, 0x5d, 0x95, 0x0b, 0x94, 0xe9, 0x2d, 0x5f, 0x87, 0x4e, 0x26, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x0b, 0x64, 0x90, 0x51, 0xbb, 0xc1, 0x16, 0x4d, 0x38, 0xeb, 0x4f, 0xd8, - 0xd6, 0x58, 0xad, 0xb7, 0x5a, 0x91, 0x71, 0x72, 0x0e, 0x73, 0xeb, 0x51, - 0xab, 0x70, 0xb2, 0x80, 0x02, 0xf3, 0xd8, 0x25, 0x4f, 0xe7, 0x14, 0xf9, - 0xff, 0x20, 0x4f, 0x9a, 0xbf, 0x00, 0x33, 0x4c, 0x79, 0x70, 0x1d, 0x97, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x03, 0x77, 0xf2, 0xe6, 0x20, 0x8f, 0xd4, 0xcb, 0x73, 0x79, 0x34, 0x5e, - 0xda, 0x55, 0x26, 0x5e, 0x55, 0xf2, 0xb8, 0xef, 0xad, 0x95, 0x3e, 0x04, - 0xe0, 0x5e, 0x2f, 0xbd, 0x15, 0xa8, 0x04, 0xe0, 0xc1, 0x96, 0xc2, 0x51, - 0x34, 0x77, 0xf8, 0x87, 0x92, 0x25, 0x81, 0x42, 0x53, 0xd7, 0xdf, 0x75, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point in an invalid subgroup - // There is only one r-order subgroup, as r does not divide the cofactor. - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x17, 0x76, 0x2a, 0x3b, 0x91, 0x08, 0xc4, 0xa7, 0x4a, 0x15, 0x1b, 0x73, - 0x2a, 0x60, 0x75, 0xbf, 0x21, 0x99, 0xbc, 0x19, 0xc4, 0x8c, 0x39, 0x3d, - 0x4c, 0xeb, 0x92, 0xd0, 0xa7, 0x60, 0x57, 0xbe, 0x02, 0xf0, 0x85, 0x40, - 0x77, 0x0f, 0xab, 0xd6, 0x02, 0x62, 0xce, 0xa7, 0x3e, 0xa1, 0x90, 0x6c, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x58, 0xb0, 0x08, 0x3c, 0x00, 0x04, 0x62, 0x72, 0xa9, 0xb6, 0x35, - 0x83, 0x96, 0x3f, 0xff, 0x07, 0xe1, 0x47, 0xf3, 0xf9, 0xe6, 0xe2, 0x41, - 0x74, 0x32, 0x8a, 0xd8, 0xbc, 0x2a, 0xa1, 0x50, 0x29, 0x8f, 0x31, 0x89, - 0xa9, 0xcf, 0x6e, 0xd6, 0x26, 0xf4, 0x61, 0xe9, 0x44, 0xbb, 0xd3, 0xd1, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x16, 0x60, 0xf9, 0x34, 0x34, 0x58, 0x8f, 0x8d, 0x3c, 0xcf, 0xb9, 0x7b, - 0x92, 0x4d, 0xce, 0xa8, 0x68, 0xca, 0xd1, 0x94, 0x30, 0x70, 0x6b, 0x4d, - 0x43, 0x93, 0x9b, 0x11, 0x99, 0x7b, 0x19, 0x43, 0x55, 0xd4, 0x2e, 0xdc, - 0x1d, 0xc4, 0x6b, 0xa0, 0x91, 0xfb, 0x0b, 0x22, 0x5e, 0xcf, 0x10, 0x3b, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x16, 0x99, 0xee, 0x57, 0x7c, 0x61, 0xb6, 0x94, 0xbe, 0xb8, 0x81, 0x37, - 0xcf, 0x34, 0xf3, 0xe7, 0x39, 0x40, 0xa2, 0xdb, 0xb9, 0x14, 0xb5, 0x29, - 0x61, 0x8b, 0xd2, 0xac, 0x32, 0x71, 0xac, 0x42, 0xc1, 0xe9, 0x85, 0xd6, - 0xd8, 0x98, 0xd9, 0xf4, 0xaa, 0xed, 0x39, 0x85, 0xb6, 0xdc, 0xb9, 0xc7, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - } - - #[test] - fn test_g2_addition_correctness() { - let mut p = G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, - 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, - 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, - 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, - 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, - 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, - 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, - 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, - 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, - 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, - 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, - 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, - 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, - ])) - .unwrap(), - }, - z: Fq2::one(), - }; - - p.add_assign(&G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x0a, 0x33, 0xd2, 0x7a, 0xdd, 0x5e, 0x7e, 0x82, 0x27, 0xc5, 0x46, 0xf7, 0x5e, - 0xe1, 0xf3, 0xab, 0x8e, 0x73, 0xa9, 0x6b, 0x32, 0x9a, 0xd1, 0x90, 0x06, 0x11, - 0x5f, 0xcc, 0x12, 0xe2, 0x76, 0x9e, 0x40, 0x87, 0x77, 0xb3, 0x0c, 0xa3, 0xad, - 0xd4, 0xa8, 0xc7, 0x63, 0xd2, 0x59, 0x10, 0xbd, 0xd3, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x14, 0x1e, 0xcb, 0xac, 0x1d, 0xeb, 0x03, 0x8b, 0x82, 0x8e, 0x58, 0x48, 0xcd, - 0x48, 0xea, 0x66, 0x20, 0x89, 0xfa, 0xf4, 0x62, 0x43, 0x82, 0x96, 0x82, 0x70, - 0xdc, 0xa3, 0xa9, 0x12, 0x40, 0x7b, 0xf1, 0x57, 0x83, 0x00, 0xe1, 0x34, 0x2e, - 0x11, 0x93, 0xb1, 0xeb, 0xcd, 0x54, 0x87, 0x0d, 0xfe, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x16, 0x57, 0x6c, 0xcd, 0x3d, 0xd0, 0xa4, 0xe8, 0xd5, 0xee, 0x2a, 0xba, 0x84, - 0xfd, 0x10, 0xfe, 0x27, 0x67, 0x03, 0x2f, 0xc3, 0x7c, 0xc3, 0x1d, 0xe8, 0xd8, - 0x10, 0x21, 0x75, 0xf5, 0xdc, 0x19, 0x8c, 0x15, 0x74, 0x22, 0x87, 0x57, 0xca, - 0x23, 0xf5, 0xd2, 0xc2, 0x88, 0x57, 0x22, 0x9c, 0x3f, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x11, 0xad, 0x23, 0x6b, 0x9b, 0xa0, 0x29, 0x90, 0xab, 0xab, 0x04, 0x0d, 0xdb, - 0xd0, 0x97, 0xcc, 0x31, 0x89, 0x8d, 0xb6, 0x3f, 0x87, 0x36, 0x3a, 0xbc, 0x15, - 0x07, 0x12, 0xf9, 0xff, 0xe6, 0xda, 0x96, 0x57, 0xf7, 0xda, 0x77, 0xf1, 0x65, - 0x0e, 0x4d, 0xa9, 0xb6, 0xf6, 0xa9, 0x6d, 0x1d, 0xd2, - ])) - .unwrap(), - }, - z: Fq2::one(), - }); - - let p = G2Affine::from(p); - - assert_eq!( - p, - G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xd7, 0xc2, 0x04, 0x56, 0x61, 0x7e, 0x89, 0xab, 0xab, 0xd7, 0x60, - 0xff, 0x05, 0xcb, 0x92, 0xf1, 0x27, 0x3e, 0x64, 0x06, 0xee, 0xf9, 0xcc, - 0xa7, 0xde, 0x72, 0xb7, 0xdd, 0x0e, 0x64, 0xb7, 0xfc, 0x64, 0x2e, 0xb3, - 0x59, 0x75, 0xb0, 0x69, 0xcd, 0xe7, 0xee, 0x8a, 0x3f, 0x2a, 0xc8, 0xaf, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0xa3, 0xb5, 0x9d, 0x29, 0xa3, 0x12, 0x74, 0xc8, 0xa0, 0xb7, 0x30, - 0xbb, 0xb2, 0x1f, 0x5e, 0x8b, 0x20, 0x32, 0x84, 0xc5, 0x1e, 0xdf, 0x6b, - 0x4d, 0xbe, 0x92, 0x4f, 0xe5, 0xfd, 0x6a, 0xc2, 0x23, 0x8f, 0x0a, 0xc6, - 0x11, 0x9d, 0x07, 0xdf, 0xd1, 0xa5, 0x0b, 0x85, 0x72, 0xcb, 0xd2, 0xb8, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x04, 0xcb, 0x84, 0x74, 0x1f, 0x3c, 0xaf, 0xe8, 0x15, 0x93, 0x84, 0x33, - 0x3d, 0x7c, 0xba, 0x97, 0x64, 0x52, 0x8a, 0xb3, 0x86, 0x36, 0x33, 0xdc, - 0x6d, 0x1e, 0xf3, 0x32, 0x48, 0x6f, 0x5e, 0x34, 0xd3, 0x09, 0x21, 0xc9, - 0x3e, 0xc3, 0x42, 0xf4, 0x9e, 0x70, 0x9e, 0x78, 0xa8, 0xea, 0xa4, 0xc9, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x03, 0xc0, 0x75, 0xd3, 0xec, 0x52, 0xba, 0x90, 0xb6, 0x88, 0x4d, 0xee, - 0xc5, 0x9f, 0xb2, 0x1f, 0x38, 0x52, 0x8f, 0x92, 0xb6, 0x89, 0x64, 0x4d, - 0x2b, 0xd7, 0xca, 0x7f, 0x43, 0x46, 0xf9, 0xec, 0xe9, 0x0a, 0x73, 0xad, - 0x65, 0xc6, 0x69, 0x19, 0x24, 0x2a, 0xf0, 0xdc, 0x36, 0x40, 0xe1, 0xa4, - ])) - .unwrap(), - }, - infinity: false, - } - ); - } - - #[test] - fn test_g2_doubling_correctness() { - let p = G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, - 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, - 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, - 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, - 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, - 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, - 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, - 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, - 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, - 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, - 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, - 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, - 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, - ])) - .unwrap(), - }, - z: Fq2::one(), - }; - - let p = G2Affine::from(p.double()); - - assert_eq!( - p, - G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x18, 0xba, 0xb7, 0x37, 0x60, 0xfd, 0x80, 0x24, 0x97, 0x55, 0xd4, 0xa3, - 0x92, 0x6e, 0x98, 0x62, 0xbc, 0xed, 0xcf, 0xce, 0x1e, 0x52, 0xd9, 0x86, - 0x11, 0x6a, 0xee, 0x59, 0x43, 0x4d, 0xe9, 0x02, 0x91, 0xa6, 0xcb, 0x18, - 0x24, 0x38, 0xfa, 0xd7, 0x91, 0xcc, 0xb1, 0x29, 0x27, 0x27, 0xc4, 0x04, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x00, 0xf1, 0x36, 0xe4, 0x39, 0x09, 0xfc, 0xa0, 0x7b, 0x4c, 0x2b, 0xae, - 0x8d, 0xb6, 0xe7, 0x0b, 0xeb, 0x0c, 0xf5, 0xe6, 0x10, 0xef, 0x4f, 0xe7, - 0xc7, 0x4d, 0x1c, 0xf4, 0xef, 0x2d, 0x59, 0x26, 0x96, 0xe5, 0x82, 0xa2, - 0x7f, 0x02, 0x89, 0x61, 0x4e, 0x7c, 0x5e, 0x0a, 0x2a, 0xe5, 0xb9, 0x9e, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x08, 0x1a, 0x53, 0xfe, 0x53, 0x1d, 0x64, 0xef, 0x8b, 0x92, 0x86, 0x6b, - 0xc6, 0x38, 0x41, 0x88, 0xa5, 0xa2, 0xa5, 0x1f, 0x7f, 0xde, 0x78, 0x7b, - 0x85, 0x3b, 0xb1, 0xd2, 0x88, 0x77, 0x57, 0x7e, 0x3e, 0xe4, 0x2e, 0xec, - 0x61, 0x4c, 0xf8, 0x90, 0x09, 0x54, 0xd4, 0x46, 0x6a, 0xb1, 0x3e, 0x58, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x19, 0xe2, 0xde, 0xae, 0x6e, 0xb9, 0xb4, 0x41, 0x24, 0x4e, 0x6c, 0x20, - 0x15, 0xc8, 0x33, 0x48, 0xb2, 0x71, 0xf5, 0x2f, 0x12, 0xea, 0xd7, 0x42, - 0x33, 0x71, 0x67, 0xee, 0x6e, 0x8e, 0x3c, 0xb6, 0xed, 0xdb, 0x5f, 0x48, - 0x30, 0x4d, 0x14, 0xb3, 0x4c, 0x5d, 0x60, 0x76, 0x66, 0x23, 0x9b, 0x34, - ])) - .unwrap(), - }, - infinity: false, - } - ); - } - - #[test] - fn g2_curve_tests() { - use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests}; - curve_tests::(); - random_wnaf_tests::(); - random_uncompressed_encoding_tests::(); - } -} - -pub use self::g1::*; -pub use self::g2::*; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs deleted file mode 100644 index 21ae050f28..0000000000 --- a/pairing/src/bls12_381/fq.rs +++ /dev/null @@ -1,1824 +0,0 @@ -use super::fq2::Fq2; -use ff::{Field, PrimeField}; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -#[cfg(test)] -use std::ops::Neg; - -// B coefficient of BLS12-381 curve, 4. -pub const B_COEFF: Fq = Fq([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, -]); - -// The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, -// and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the -// result is nonzero. - -// Generator of G1 -// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 -// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -pub const G1_GENERATOR_X: Fq = Fq([ - 0x5cb38790fd530c16, - 0x7817fc679976fff5, - 0x154f95c7143ba1c1, - 0xf0ae6acdf3d0e747, - 0xedce6ecc21dbf440, - 0x120177419e0bfb75, -]); -pub const G1_GENERATOR_Y: Fq = Fq([ - 0xbaac93d50ce72271, - 0x8c22631a7918fd8e, - 0xdd595f13570725ce, - 0x51ac582950405194, - 0xe1c8c3fad0059c0, - 0xbbc3efc5008a26a, -]); - -// Generator of G2 -// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 -// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 -pub const G2_GENERATOR_X_C0: Fq = Fq([ - 0xf5f28fa202940a10, - 0xb3f5fb2687b4961a, - 0xa1a893b53e2ae580, - 0x9894999d1a3caee9, - 0x6f67b7631863366b, - 0x58191924350bcd7, -]); -pub const G2_GENERATOR_X_C1: Fq = Fq([ - 0xa5a9c0759e23f606, - 0xaaa0c59dbccd60c3, - 0x3bb17e18e2867806, - 0x1b1ab6cc8541b367, - 0xc2b6ed0ef2158547, - 0x11922a097360edf3, -]); -pub const G2_GENERATOR_Y_C0: Fq = Fq([ - 0x4c730af860494c4a, - 0x597cfa1f5e369c5a, - 0xe7e6856caa0a635a, - 0xbbefb5e96e0d495f, - 0x7d3a975f0ef25a2, - 0x83fd8e7e80dae5, -]); -pub const G2_GENERATOR_Y_C1: Fq = Fq([ - 0xadc0fc92df64b05d, - 0x18aa270a2b1461dc, - 0x86adac6a3be4eba0, - 0x79495c4ec93da33a, - 0xe7175850a43ccaed, - 0xb2bc2a163de1bf2, -]); - -// Coefficients for the Frobenius automorphism. -pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ - // Fq(-1)**(((q^0) - 1) / 2) - Fq([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ]), - // Fq(-1)**(((q^1) - 1) / 2) - Fq([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ]), -]; - -pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ - // Fq2(u + 1)**(((q^0) - 1) / 3) - Fq2 { - c0: Fq([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^1) - 1) / 3) - Fq2 { - c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - c1: Fq([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]), - }, - // Fq2(u + 1)**(((q^2) - 1) / 3) - Fq2 { - c0: Fq([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^3) - 1) / 3) - Fq2 { - c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - c1: Fq([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ]), - }, - // Fq2(u + 1)**(((q^4) - 1) / 3) - Fq2 { - c0: Fq([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^5) - 1) / 3) - Fq2 { - c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - c1: Fq([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ]), - }, -]; - -pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ - // Fq2(u + 1)**(((2q^0) - 2) / 3) - Fq2 { - c0: Fq([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^1) - 2) / 3) - Fq2 { - c0: Fq([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^2) - 2) / 3) - Fq2 { - c0: Fq([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^3) - 2) / 3) - Fq2 { - c0: Fq([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^4) - 2) / 3) - Fq2 { - c0: Fq([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^5) - 2) / 3) - Fq2 { - c0: Fq([ - 0xecfb361b798dba3a, - 0xc100ddb891865a2c, - 0xec08ff1232bda8e, - 0xd5c13cc6f1ca4721, - 0x47222a47bf7b5c04, - 0x110f184e51c5f59, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, -]; - -// non_residue^((modulus^i-1)/6) for i=0,...,11 -pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ - // Fq2(u + 1)**(((q^0) - 1) / 6) - Fq2 { - c0: Fq([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^1) - 1) / 6) - Fq2 { - c0: Fq([ - 0x7089552b319d465, - 0xc6695f92b50a8313, - 0x97e83cccd117228f, - 0xa35baecab2dc29ee, - 0x1ce393ea5daace4d, - 0x8f2220fb0fb66eb, - ]), - c1: Fq([ - 0xb2f66aad4ce5d646, - 0x5842a06bfc497cec, - 0xcf4895d42599d394, - 0xc11b9cba40a8e8d0, - 0x2e3813cbe5a0de89, - 0x110eefda88847faf, - ]), - }, - // Fq2(u + 1)**(((q^2) - 1) / 6) - Fq2 { - c0: Fq([ - 0xecfb361b798dba3a, - 0xc100ddb891865a2c, - 0xec08ff1232bda8e, - 0xd5c13cc6f1ca4721, - 0x47222a47bf7b5c04, - 0x110f184e51c5f59, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^3) - 1) / 6) - Fq2 { - c0: Fq([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0xbd592fc7d825ec8, - ]), - c1: Fq([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0xe2b7eedbbfd87d2, - ]), - }, - // Fq2(u + 1)**(((q^4) - 1) / 6) - Fq2 { - c0: Fq([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^5) - 1) / 6) - Fq2 { - c0: Fq([ - 0x3726c30af242c66c, - 0x7c2ac1aad1b6fe70, - 0xa04007fbba4b14a2, - 0xef517c3266341429, - 0x95ba654ed2226b, - 0x2e370eccc86f7dd, - ]), - c1: Fq([ - 0x82d83cf50dbce43f, - 0xa2813e53df9d018f, - 0xc6f0caa53c65e181, - 0x7525cf528d50fe95, - 0x4a85ed50f4798a6b, - 0x171da0fd6cf8eebd, - ]), - }, - // Fq2(u + 1)**(((q^6) - 1) / 6) - Fq2 { - c0: Fq([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^7) - 1) / 6) - Fq2 { - c0: Fq([ - 0xb2f66aad4ce5d646, - 0x5842a06bfc497cec, - 0xcf4895d42599d394, - 0xc11b9cba40a8e8d0, - 0x2e3813cbe5a0de89, - 0x110eefda88847faf, - ]), - c1: Fq([ - 0x7089552b319d465, - 0xc6695f92b50a8313, - 0x97e83cccd117228f, - 0xa35baecab2dc29ee, - 0x1ce393ea5daace4d, - 0x8f2220fb0fb66eb, - ]), - }, - // Fq2(u + 1)**(((q^8) - 1) / 6) - Fq2 { - c0: Fq([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^9) - 1) / 6) - Fq2 { - c0: Fq([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0xe2b7eedbbfd87d2, - ]), - c1: Fq([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0xbd592fc7d825ec8, - ]), - }, - // Fq2(u + 1)**(((q^10) - 1) / 6) - Fq2 { - c0: Fq([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((q^11) - 1) / 6) - Fq2 { - c0: Fq([ - 0x82d83cf50dbce43f, - 0xa2813e53df9d018f, - 0xc6f0caa53c65e181, - 0x7525cf528d50fe95, - 0x4a85ed50f4798a6b, - 0x171da0fd6cf8eebd, - ]), - c1: Fq([ - 0x3726c30af242c66c, - 0x7c2ac1aad1b6fe70, - 0xa04007fbba4b14a2, - 0xef517c3266341429, - 0x95ba654ed2226b, - 0x2e370eccc86f7dd, - ]), - }, -]; - -// -((2**384) mod q) mod q -pub const NEGATIVE_ONE: Fq = Fq([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, -]); - -#[derive(PrimeField)] -#[PrimeFieldModulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787"] -#[PrimeFieldGenerator = "2"] -#[PrimeFieldReprEndianness = "big"] -pub struct Fq([u64; 6]); - -#[test] -fn test_b_coeff() { - assert_eq!(Fq::from(4), B_COEFF); -} - -#[test] -#[allow(clippy::cognitive_complexity)] -fn test_frob_coeffs() { - let nqr = Fq::one().neg(); - - assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one()); - assert_eq!( - FROBENIUS_COEFF_FQ2_C1[1], - nqr.pow_vartime([ - 0xdcff7fffffffd555u64, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d - ]) - ); - - let nqr = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[1], - nqr.pow_vartime([ - 0x9354ffffffffe38eu64, - 0xa395554e5c6aaaa, - 0xcd104635a790520c, - 0xcc27c3d6fbd7063f, - 0x190937e76bc3e447, - 0x8ab05f8bdd54cde - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[2], - nqr.pow_vartime([ - 0xb78e0000097b2f68u64, - 0xd44f23b47cbd64e3, - 0x5cb9668120b069a9, - 0xccea85f9bf7b3d16, - 0xdba2c8d7adb356d, - 0x9cd75ded75d7429, - 0xfc65c31103284fab, - 0xc58cb9a9b249ee24, - 0xccf734c3118a2e9a, - 0xa0f4304c5a256ce6, - 0xc3f0d2f8e0ba61f8, - 0xe167e192ebca97 - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[3], - nqr.pow_vartime([ - 0xdbc6fcd6f35b9e06u64, - 0x997dead10becd6aa, - 0x9dbbd24c17206460, - 0x72b97acc6057c45e, - 0xf8e9a230bf0c628e, - 0x647ccb1885c63a7, - 0xce80264fc55bf6ee, - 0x94d8d716c3939fc4, - 0xad78f0eb77ee6ee1, - 0xd6fe49bfe57dc5f9, - 0x2656d6c15c63647, - 0xdf6282f111fa903, - 0x1bdba63e0632b4bb, - 0x6883597bcaa505eb, - 0xa56d4ec90c34a982, - 0x7e4c42823bbe90b2, - 0xf64728aa6dcb0f20, - 0x16e57e16ef152f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[4], - nqr.pow_vartime([ - 0x4649add3c71c6d90u64, - 0x43caa6528972a865, - 0xcda8445bbaaa0fbb, - 0xc93dea665662aa66, - 0x2863bc891834481d, - 0x51a0c3f5d4ccbed8, - 0x9210e660f90ccae9, - 0xe2bd6836c546d65e, - 0xf223abbaa7cf778b, - 0xd4f10b222cf11680, - 0xd540f5eff4a1962e, - 0xa123a1f140b56526, - 0x31ace500636a59f6, - 0x3a82bc8c8dfa57a9, - 0x648c511e217fc1f8, - 0x36c17ffd53a4558f, - 0x881bef5fd684eefd, - 0x5d648dbdc5dbb522, - 0x8fd07bf06e5e59b8, - 0x8ddec8a9acaa4b51, - 0x4cc1f8688e2def26, - 0xa74e63cb492c03de, - 0x57c968173d1349bb, - 0x253674e02a866 - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[5], - nqr.pow_vartime([ - 0xf896f792732eb2beu64, - 0x49c86a6d1dc593a1, - 0xe5b31e94581f91c3, - 0xe3da5cc0a6b20d7f, - 0x822caef950e0bfed, - 0x317ed950b9ee67cd, - 0xffd664016ee3f6cd, - 0x77d991c88810b122, - 0x62e72e635e698264, - 0x905e1a1a2d22814a, - 0xf5b7ab3a3f33d981, - 0x175871b0bc0e25dd, - 0x1e2e9a63df5c3772, - 0xe888b1f7445b149d, - 0x9551c19e5e7e2c24, - 0xecf21939a3d2d6be, - 0xd830dbfdab72dbd4, - 0x7b34af8d622d40c0, - 0x3df6d20a45671242, - 0xaf86bee30e21d98, - 0x41064c1534e5df5d, - 0xf5f6cabd3164c609, - 0xa5d14bdf2b7ee65, - 0xa718c069defc9138, - 0xdb1447e770e3110e, - 0xc1b164a9e90af491, - 0x7180441f9d251602, - 0x1fd3a5e6a9a893e, - 0x1e17b779d54d5db, - 0x3c7afafe3174 - ]) - ); - - assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[1], - nqr.pow_vartime([ - 0x26a9ffffffffc71cu64, - 0x1472aaa9cb8d5555, - 0x9a208c6b4f20a418, - 0x984f87adf7ae0c7f, - 0x32126fced787c88f, - 0x11560bf17baa99bc - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[2], - nqr.pow_vartime([ - 0x6f1c000012f65ed0u64, - 0xa89e4768f97ac9c7, - 0xb972cd024160d353, - 0x99d50bf37ef67a2c, - 0x1b74591af5b66adb, - 0x139aebbdaebae852, - 0xf8cb862206509f56, - 0x8b1973536493dc49, - 0x99ee698623145d35, - 0x41e86098b44ad9cd, - 0x87e1a5f1c174c3f1, - 0x1c2cfc325d7952f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[3], - nqr.pow_vartime([ - 0xb78df9ade6b73c0cu64, - 0x32fbd5a217d9ad55, - 0x3b77a4982e40c8c1, - 0xe572f598c0af88bd, - 0xf1d344617e18c51c, - 0xc8f996310b8c74f, - 0x9d004c9f8ab7eddc, - 0x29b1ae2d87273f89, - 0x5af1e1d6efdcddc3, - 0xadfc937fcafb8bf3, - 0x4cadad82b8c6c8f, - 0x1bec505e223f5206, - 0x37b74c7c0c656976, - 0xd106b2f7954a0bd6, - 0x4ada9d9218695304, - 0xfc988504777d2165, - 0xec8e5154db961e40, - 0x2dcafc2dde2a5f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[4], - nqr.pow_vartime([ - 0x8c935ba78e38db20u64, - 0x87954ca512e550ca, - 0x9b5088b775541f76, - 0x927bd4ccacc554cd, - 0x50c779123068903b, - 0xa34187eba9997db0, - 0x2421ccc1f21995d2, - 0xc57ad06d8a8dacbd, - 0xe44757754f9eef17, - 0xa9e2164459e22d01, - 0xaa81ebdfe9432c5d, - 0x424743e2816aca4d, - 0x6359ca00c6d4b3ed, - 0x750579191bf4af52, - 0xc918a23c42ff83f0, - 0x6d82fffaa748ab1e, - 0x1037debfad09ddfa, - 0xbac91b7b8bb76a45, - 0x1fa0f7e0dcbcb370, - 0x1bbd9153595496a3, - 0x9983f0d11c5bde4d, - 0x4e9cc796925807bc, - 0xaf92d02e7a269377, - 0x4a6ce9c0550cc - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[5], - nqr.pow_vartime([ - 0xf12def24e65d657cu64, - 0x9390d4da3b8b2743, - 0xcb663d28b03f2386, - 0xc7b4b9814d641aff, - 0x4595df2a1c17fdb, - 0x62fdb2a173dccf9b, - 0xffacc802ddc7ed9a, - 0xefb3239110216245, - 0xc5ce5cc6bcd304c8, - 0x20bc34345a450294, - 0xeb6f56747e67b303, - 0x2eb0e361781c4bbb, - 0x3c5d34c7beb86ee4, - 0xd11163ee88b6293a, - 0x2aa3833cbcfc5849, - 0xd9e4327347a5ad7d, - 0xb061b7fb56e5b7a9, - 0xf6695f1ac45a8181, - 0x7beda4148ace2484, - 0x15f0d7dc61c43b30, - 0x820c982a69cbbeba, - 0xebed957a62c98c12, - 0x14ba297be56fdccb, - 0x4e3180d3bdf92270, - 0xb6288fcee1c6221d, - 0x8362c953d215e923, - 0xe300883f3a4a2c05, - 0x3fa74bcd535127c, - 0x3c2f6ef3aa9abb6, - 0x78f5f5fc62e8 - ]) - ); - - assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[1], - nqr.pow_vartime([ - 0x49aa7ffffffff1c7u64, - 0x51caaaa72e35555, - 0xe688231ad3c82906, - 0xe613e1eb7deb831f, - 0xc849bf3b5e1f223, - 0x45582fc5eeaa66f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[2], - nqr.pow_vartime([ - 0xdbc7000004bd97b4u64, - 0xea2791da3e5eb271, - 0x2e5cb340905834d4, - 0xe67542fcdfbd9e8b, - 0x86dd1646bd6d9ab6, - 0x84e6baef6baeba14, - 0x7e32e188819427d5, - 0x62c65cd4d924f712, - 0x667b9a6188c5174d, - 0x507a18262d12b673, - 0xe1f8697c705d30fc, - 0x70b3f0c975e54b - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[3], - nqr.pow_vartime(vec![ - 0x6de37e6b79adcf03u64, - 0x4cbef56885f66b55, - 0x4edde9260b903230, - 0x395cbd66302be22f, - 0xfc74d1185f863147, - 0x323e658c42e31d3, - 0x67401327e2adfb77, - 0xca6c6b8b61c9cfe2, - 0xd6bc7875bbf73770, - 0xeb7f24dff2bee2fc, - 0x8132b6b60ae31b23, - 0x86fb1417888fd481, - 0x8dedd31f03195a5d, - 0x3441acbde55282f5, - 0x52b6a764861a54c1, - 0x3f2621411ddf4859, - 0xfb23945536e58790, - 0xb72bf0b778a97, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[4], - nqr.pow_vartime(vec![ - 0xa324d6e9e38e36c8u64, - 0xa1e5532944b95432, - 0x66d4222ddd5507dd, - 0xe49ef5332b315533, - 0x1431de448c1a240e, - 0xa8d061faea665f6c, - 0x490873307c866574, - 0xf15eb41b62a36b2f, - 0x7911d5dd53e7bbc5, - 0x6a78859116788b40, - 0x6aa07af7fa50cb17, - 0x5091d0f8a05ab293, - 0x98d6728031b52cfb, - 0x1d415e4646fd2bd4, - 0xb246288f10bfe0fc, - 0x9b60bffea9d22ac7, - 0x440df7afeb42777e, - 0x2eb246dee2edda91, - 0xc7e83df8372f2cdc, - 0x46ef6454d65525a8, - 0x2660fc344716f793, - 0xd3a731e5a49601ef, - 0x2be4b40b9e89a4dd, - 0x129b3a7015433, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[5], - nqr.pow_vartime(vec![ - 0xfc4b7bc93997595fu64, - 0xa4e435368ee2c9d0, - 0xf2d98f4a2c0fc8e1, - 0xf1ed2e60535906bf, - 0xc116577ca8705ff6, - 0x98bf6ca85cf733e6, - 0x7feb3200b771fb66, - 0x3becc8e444085891, - 0x31739731af34c132, - 0xc82f0d0d169140a5, - 0xfadbd59d1f99ecc0, - 0xbac38d85e0712ee, - 0x8f174d31efae1bb9, - 0x744458fba22d8a4e, - 0x4aa8e0cf2f3f1612, - 0x76790c9cd1e96b5f, - 0x6c186dfed5b96dea, - 0x3d9a57c6b116a060, - 0x1efb690522b38921, - 0x857c35f718710ecc, - 0xa083260a9a72efae, - 0xfafb655e98b26304, - 0x52e8a5ef95bf732, - 0x538c6034ef7e489c, - 0xed8a23f3b8718887, - 0x60d8b254f4857a48, - 0x38c0220fce928b01, - 0x80fe9d2f354d449f, - 0xf0bdbbceaa6aed, - 0x1e3d7d7f18ba, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[6], - nqr.pow_vartime(vec![ - 0x21219610a012ba3cu64, - 0xa5c19ad35375325, - 0x4e9df1e497674396, - 0xfb05b717c991c6ef, - 0x4a1265bca93a32f2, - 0xd875ff2a7bdc1f66, - 0xc6d8754736c771b2, - 0x2d80c759ba5a2ae7, - 0x138a20df4b03cc1a, - 0xc22d07fe68e93024, - 0xd1dc474d3b433133, - 0xc22aa5e75044e5c, - 0xf657c6fbf9c17ebf, - 0xc591a794a58660d, - 0x2261850ee1453281, - 0xd17d3bd3b7f5efb4, - 0xf00cec8ec507d01, - 0x2a6a775657a00ae6, - 0x5f098a12ff470719, - 0x409d194e7b5c5afa, - 0x1d66478e982af5b, - 0xda425a5b5e01ca3f, - 0xf77e4f78747e903c, - 0x177d49f73732c6fc, - 0xa9618fecabe0e1f4, - 0xba5337eac90bd080, - 0x66fececdbc35d4e7, - 0xa4cd583203d9206f, - 0x98391632ceeca596, - 0x4946b76e1236ad3f, - 0xa0dec64e60e711a1, - 0xfcb41ed3605013, - 0x8ca8f9692ae1e3a9, - 0xd3078bfc28cc1baf, - 0xf0536f764e982f82, - 0x3125f1a2656, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[7], - nqr.pow_vartime(vec![ - 0x742754a1f22fdbu64, - 0x2a1955c2dec3a702, - 0x9747b28c796d134e, - 0xc113a0411f59db79, - 0x3bb0fa929853bfc1, - 0x28c3c25f8f6fb487, - 0xbc2b6c99d3045b34, - 0x98fb67d6badde1fd, - 0x48841d76a24d2073, - 0xd49891145fe93ae6, - 0xc772b9c8e74d4099, - 0xccf4e7b9907755bb, - 0x9cf47b25d42fd908, - 0x5616a0c347fc445d, - 0xff93b7a7ad1b8a6d, - 0xac2099256b78a77a, - 0x7804a95b02892e1c, - 0x5cf59ca7bfd69776, - 0xa7023502acd3c866, - 0xc76f4982fcf8f37, - 0x51862a5a57ac986e, - 0x38b80ed72b1b1023, - 0x4a291812066a61e1, - 0xcd8a685eff45631, - 0x3f40f708764e4fa5, - 0x8aa0441891285092, - 0x9eff60d71cdf0a9, - 0x4fdd9d56517e2bfa, - 0x1f3c80d74a28bc85, - 0x24617417c064b648, - 0x7ddda1e4385d5088, - 0xf9e132b11dd32a16, - 0xcc957cb8ef66ab99, - 0xd4f206d37cb752c5, - 0x40de343f28ad616b, - 0x8d1f24379068f0e3, - 0x6f31d7947ea21137, - 0x27311f9c32184061, - 0x9eea0664cc78ce5f, - 0x7d4151f6fea9a0da, - 0x454096fa75bd571a, - 0x4fe0f20ecb, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[8], - nqr.pow_vartime(vec![ - 0x802f5720d0b25710u64, - 0x6714f0a258b85c7c, - 0x31394c90afdf16e, - 0xe9d2b0c64f957b19, - 0xe67c0d9c5e7903ee, - 0x3156fdc5443ea8ef, - 0x7c4c50524d88c892, - 0xc99dc8990c0ad244, - 0xd37ababf3649a896, - 0x76fe4b838ff7a20c, - 0xcf69ee2cec728db3, - 0xb83535548e5f41, - 0x371147684ccb0c23, - 0x194f6f4fa500db52, - 0xc4571dc78a4c5374, - 0xe4d46d479999ca97, - 0x76b6785a615a151c, - 0xcceb8bcea7eaf8c1, - 0x80d87a6fbe5ae687, - 0x6a97ddddb85ce85, - 0xd783958f26034204, - 0x7144506f2e2e8590, - 0x948693d377aef166, - 0x8364621ed6f96056, - 0xf021777c4c09ee2d, - 0xc6cf5e746ecd50b, - 0xa2337b7aa22743df, - 0xae753f8bbacab39c, - 0xfc782a9e34d3c1cc, - 0x21b827324fe494d9, - 0x5692ce350ed03b38, - 0xf323a2b3cd0481b0, - 0xe859c97a4ccad2e3, - 0x48434b70381e4503, - 0x46042d62e4132ed8, - 0x48c4d6f56122e2f2, - 0xf87711ab9f5c1af7, - 0xb14b7a054759b469, - 0x8eb0a96993ffa9aa, - 0x9b21fb6fc58b760c, - 0xf3abdd115d2e7d25, - 0xf7beac3d4d12409c, - 0x40a5585cce69bf03, - 0x697881e1ba22d5a8, - 0x3d6c04e6ad373fd9, - 0x849871bf627be886, - 0x550f4b9b71b28ef9, - 0x81d2e0d78, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[9], - nqr.pow_vartime(vec![ - 0x4af4accf7de0b977u64, - 0x742485e21805b4ee, - 0xee388fbc4ac36dec, - 0x1e199da57ad178a, - 0xc27c12b292c6726a, - 0x162e6ed84505b5e8, - 0xe191683f336e09df, - 0x17deb7e8d1e0fce6, - 0xd944f19ad06f5836, - 0x4c5f5e59f6276026, - 0xf1ba9c7c148a38a8, - 0xd205fe2dba72b326, - 0x9a2cf2a4c289824e, - 0x4f47ad512c39e24d, - 0xc5894d984000ea09, - 0x2974c03ff7cf01fa, - 0xfcd243b48cb99a22, - 0x2b5150c9313ac1e8, - 0x9089f37c7fc80eda, - 0x989540cc9a7aea56, - 0x1ab1d4e337e63018, - 0x42b546c30d357e43, - 0x1c6abc04f76233d9, - 0x78b3b8d88bf73e47, - 0x151c4e4c45dc68e6, - 0x519a79c4f54397ed, - 0x93f5b51535a127c5, - 0x5fc51b6f52fa153e, - 0x2e0504f2d4a965c3, - 0xc85bd3a3da52bffe, - 0x98c60957a46a89ef, - 0x48c03b5976b91cae, - 0xc6598040a0a61438, - 0xbf0b49dc255953af, - 0xb78dff905b628ab4, - 0x68140b797ba74ab8, - 0x116cf037991d1143, - 0x2f7fe82e58acb0b8, - 0xc20bf7a8f7be5d45, - 0x86c2905c338d5709, - 0xff13a3ae6c8ace3d, - 0xb6f95e2282d08337, - 0xd49f7b313e9cbf29, - 0xf794517193a1ce8c, - 0x39641fecb596a874, - 0x411c4c4edf462fb3, - 0x3f8cd55c10cf25b4, - 0x2bdd7ea165e860b6, - 0xacd7d2cef4caa193, - 0x6558a1d09a05f96, - 0x1f52b5f5b546fc20, - 0x4ee22a5a8c250c12, - 0xd3a63a54a205b6b3, - 0xd2ff5be8, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[10], - nqr.pow_vartime(vec![ - 0xe5953a4f96cdda44u64, - 0x336b2d734cbc32bb, - 0x3f79bfe3cd7410e, - 0x267ae19aaa0f0332, - 0x85a9c4db78d5c749, - 0x90996b046b5dc7d8, - 0x8945eae9820afc6a, - 0x2644ddea2b036bd, - 0x39898e35ac2e3819, - 0x2574eab095659ab9, - 0x65953d51ac5ea798, - 0xc6b8c7afe6752466, - 0x40e9e993e9286544, - 0x7e0ad34ad9700ea0, - 0xac1015eba2c69222, - 0x24f057a19239b5d8, - 0x2043b48c8a3767eb, - 0x1117c124a75d7ff4, - 0x433cfd1a09fb3ce7, - 0x25b087ce4bcf7fb, - 0xbcee0dc53a3e5bdb, - 0xbffda040cf028735, - 0xf7cf103a25512acc, - 0x31d4ecda673130b9, - 0xea0906dab18461e6, - 0x5a40585a5ac3050d, - 0x803358fc14fd0eda, - 0x3678ca654eada770, - 0x7b91a1293a45e33e, - 0xcd5e5b8ea8530e43, - 0x21ae563ab34da266, - 0xecb00dad60df8894, - 0x77fe53e652facfef, - 0x9b7d1ad0b00244ec, - 0xe695df5ca73f801, - 0x23cdb21feeab0149, - 0x14de113e7ea810d9, - 0x52600cd958dac7e7, - 0xc83392c14667e488, - 0x9f808444bc1717fc, - 0x56facb4bcf7c788f, - 0x8bcad53245fc3ca0, - 0xdef661e83f27d81c, - 0x37d4ebcac9ad87e5, - 0x6fe8b24f5cdb9324, - 0xee08a26c1197654c, - 0xc98b22f65f237e9a, - 0xf54873a908ed3401, - 0x6e1cb951d41f3f3, - 0x290b2250a54e8df6, - 0x7f36d51eb1db669e, - 0xb08c7ed81a6ee43e, - 0x95e1c90fb092f680, - 0x429e4afd0e8b820, - 0x2c14a83ee87d715c, - 0xf37267575cfc8af5, - 0xb99e9afeda3c2c30, - 0x8f0f69da75792d5a, - 0x35074a85a533c73, - 0x156ed119, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[11], - nqr.pow_vartime(vec![ - 0x107db680942de533u64, - 0x6262b24d2052393b, - 0x6136df824159ebc, - 0xedb052c9970c5deb, - 0xca813aea916c3777, - 0xf49dacb9d76c1788, - 0x624941bd372933bb, - 0xa5e60c2520638331, - 0xb38b661683411074, - 0x1d2c9af4c43d962b, - 0x17d807a0f14aa830, - 0x6e6581a51012c108, - 0x668a537e5b35e6f5, - 0x6c396cf3782dca5d, - 0x33b679d1bff536ed, - 0x736cce41805d90aa, - 0x8a562f369eb680bf, - 0x9f61aa208a11ded8, - 0x43dd89dd94d20f35, - 0xcf84c6610575c10a, - 0x9f318d49cf2fe8e6, - 0xbbc6e5f25a6e434e, - 0x6528c433d11d987b, - 0xffced71cc48c0e8a, - 0x4cbb1474f4cb2a26, - 0x66a035c0b28b7231, - 0xa6f2875faa1a82ae, - 0xdd1ea3deff818b02, - 0xe0cfdf0dcdecf701, - 0x9aefa231f2f6d23, - 0xfb251297efa06746, - 0x5a40d367df985538, - 0x1ea31d69ab506fed, - 0xc64ea8280e89a73f, - 0x969acf9f2d4496f4, - 0xe84c9181ee60c52c, - 0xc60f27fc19fc6ca4, - 0x760b33d850154048, - 0x84f69080f66c8457, - 0xc0192ba0fabf640e, - 0xd2c338765c23a3a8, - 0xa7838c20f02cec6c, - 0xb7cf01d020572877, - 0xd63ffaeba0be200a, - 0xf7492baeb5f041ac, - 0x8602c5212170d117, - 0xad9b2e83a5a42068, - 0x2461829b3ba1083e, - 0x7c34650da5295273, - 0xdc824ba800a8265a, - 0xd18d9b47836af7b2, - 0x3af78945c58cbf4d, - 0x7ed9575b8596906c, - 0x6d0c133895009a66, - 0x53bc1247ea349fe1, - 0x6b3063078d41aa7a, - 0x6184acd8cd880b33, - 0x76f4d15503fd1b96, - 0x7a9afd61eef25746, - 0xce974aadece60609, - 0x88ca59546a8ceafd, - 0x6d29391c41a0ac07, - 0x443843a60e0f46a6, - 0xa1590f62fd2602c7, - 0x536d5b15b514373f, - 0x22d582b, - ]) - ); -} - -#[test] -fn test_neg_one() { - let o = Fq::one().neg(); - - assert_eq!(NEGATIVE_ONE, o); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq_is_valid() { - let mut a = MODULUS_LIMBS; - assert!(!a.is_valid()); - a.sub_noborrow(&Fq([1, 0, 0, 0, 0, 0])); - assert!(a.is_valid()); - assert!(Fq::from(0).is_valid()); - assert!(Fq([ - 0xdf4671abd14dab3e, - 0xe2dc0c9f534fbd33, - 0x31ca6c880cc444a6, - 0x257a67e70ef33359, - 0xf9b29e493f899b36, - 0x17c8be1800b9f059 - ]) - .is_valid()); - assert!(!Fq([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - .is_valid()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fq::random(&mut rng); - assert!(a.is_valid()); - } -} - -#[test] -fn test_fq_add_assign() { - { - // Random number - let mut tmp = Fq([ - 0x624434821df92b69, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b, - ]); - assert!(tmp.is_valid()); - // Test that adding zero has no effect. - tmp.add_assign(&Fq([0, 0, 0, 0, 0, 0])); - assert_eq!( - tmp, - Fq([ - 0x624434821df92b69, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b - ]) - ); - // Add one and test for the result. - tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); - assert_eq!( - tmp, - Fq([ - 0x624434821df92b6a, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b - ]) - ); - // Add another random number that exercises the reduction. - tmp.add_assign(&Fq([ - 0x374d8f8ea7a648d8, - 0xe318bb0ebb8bfa9b, - 0x613d996f0a95b400, - 0x9fac233cb7e4fef1, - 0x67e47552d253c52, - 0x5c31b227edf25da, - ])); - assert_eq!( - tmp, - Fq([ - 0xdf92c410c59fc997, - 0x149f1bd05a0add85, - 0xd3ec393c20fba6ab, - 0x37001165c1bde71d, - 0x421b41c9f662408e, - 0x21c38104f435f5b - ]) - ); - // Add one to (q - 1) and test for the result. - tmp = Fq([ - 0xb9feffffffffaaaa, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ]); - tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); - assert!(tmp.is_zero()); - // Add a random number to another one such that the result is q - 1 - tmp = Fq([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ]); - tmp.add_assign(&Fq([ - 0x66ecde5bef0fe14f, - 0xac2a6cf8aed568e8, - 0x861d70d86483edd, - 0xcc98f1b7839a22e8, - 0x6ee5e2a4eae7674e, - 0x194e40737930c599, - ])); - assert_eq!( - tmp, - Fq([ - 0xb9feffffffffaaaa, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a - ]) - ); - // Add one to the result and test for it. - tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); - assert!(tmp.is_zero()); - } - - // Test associativity - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fq::random(&mut rng); - let b = Fq::random(&mut rng); - let c = Fq::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - - let mut tmp2 = b; - tmp2.add_assign(&c); - tmp2.add_assign(&a); - - assert!(tmp1.is_valid()); - assert!(tmp2.is_valid()); - assert_eq!(tmp1, tmp2); - } -} - -#[test] -fn test_fq_sub_assign() { - { - // Test arbitrary subtraction that tests reduction. - let mut tmp = Fq([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ]); - tmp.sub_assign(&Fq([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ])); - assert_eq!( - tmp, - Fq([ - 0x748014838971292c, - 0xfd20fad49fddde5c, - 0xcf87f198e3d3f336, - 0x3d62d6e6e41883db, - 0x45a3443cd88dc61b, - 0x151d57aaf755ff94 - ]) - ); - - // Test the opposite subtraction which doesn't test reduction. - tmp = Fq([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ]); - tmp.sub_assign(&Fq([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ])); - assert_eq!( - tmp, - Fq([ - 0x457eeb7c768e817f, - 0x218b052a117621a3, - 0x97a8e10812dd02ed, - 0x2714749e0f6c8ee3, - 0x57863796abde6bc, - 0x4e3ba3f4229e706 - ]) - ); - - // Test for sensible results with zero - tmp = Fq::zero(); - tmp.sub_assign(&Fq::zero()); - assert!(tmp.is_zero()); - - tmp = Fq([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ]); - tmp.sub_assign(&Fq::zero()); - assert_eq!( - tmp, - Fq([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806 - ]) - ); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fq::random(&mut rng); - let b = Fq::random(&mut rng); - - let mut tmp1 = a; - tmp1.sub_assign(&b); - - let mut tmp2 = b; - tmp2.sub_assign(&a); - - tmp1.add_assign(&tmp2); - assert!(tmp1.is_zero()); - } -} - -#[test] -fn test_fq_mul_assign() { - let mut tmp = Fq([ - 0xcc6200000020aa8a, - 0x422800801dd8001a, - 0x7f4f5e619041c62c, - 0x8a55171ac70ed2ba, - 0x3f69cc3a3d07d58b, - 0xb972455fd09b8ef, - ]); - tmp.mul_assign(&Fq([ - 0x329300000030ffcf, - 0x633c00c02cc40028, - 0xbef70d925862a942, - 0x4f7fa2a82a963c17, - 0xdf1eb2575b8bc051, - 0x1162b680fb8e9566, - ])); - assert!( - tmp == Fq([ - 0x9dc4000001ebfe14, - 0x2850078997b00193, - 0xa8197f1abb4d7bf, - 0xc0309573f4bfe871, - 0xf48d0923ffaf7620, - 0x11d4b58c7a926e66 - ]) - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fq::random(&mut rng); - let b = Fq::random(&mut rng); - let c = Fq::random(&mut rng); - - let mut tmp1 = a; - tmp1.mul_assign(&b); - tmp1.mul_assign(&c); - - let mut tmp2 = b; - tmp2.mul_assign(&c); - tmp2.mul_assign(&a); - - assert_eq!(tmp1, tmp2); - } - - for _ in 0..1000000 { - // Ensure that r * (a + b + c) = r*a + r*b + r*c - - let r = Fq::random(&mut rng); - let mut a = Fq::random(&mut rng); - let mut b = Fq::random(&mut rng); - let mut c = Fq::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - tmp1.mul_assign(&r); - - a.mul_assign(&r); - b.mul_assign(&r); - c.mul_assign(&r); - - a.add_assign(&b); - a.add_assign(&c); - - assert_eq!(tmp1, a); - } -} - -#[test] -fn test_fq_squaring() { - let a = Fq([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x19ffffffffffffff, - ]); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fq::from_repr(FqRepr([ - 0x02, 0x4b, 0xcb, 0xe5, 0xd5, 0x1b, 0x9a, 0x6f, 0x79, 0x36, 0x1e, 0x5a, 0x80, 0x2c, - 0x6a, 0x23, 0xdc, 0x05, 0xc6, 0x59, 0xb4, 0xe1, 0x5b, 0x27, 0xcc, 0xe1, 0xd4, 0xed, - 0xc1, 0x20, 0xe6, 0x6e, 0x02, 0x4c, 0xbe, 0x17, 0x31, 0x57, 0x7a, 0x59, 0x1c, 0xfb, - 0x28, 0xfe, 0x7d, 0xfb, 0xbb, 0x86, - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fq::random(&mut rng); - assert_eq!(a.square(), a * a); - } -} - -#[test] -fn test_fq_invert() { - assert!(bool::from(Fq::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fq::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fq::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fq_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fq::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fq_neg() { - { - let a = Fq::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fq::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fq_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0u64..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fq::random(&mut rng); - let target = a.pow_vartime(&[i]); - let mut c = Fq::one(); - for _ in 0..i { - c.mul_assign(&a); - } - assert_eq!(c, target); - } - - use byteorder::ByteOrder; - let mut char_limbs = [0; 6]; - byteorder::BigEndian::read_u64_into(Fq::char().as_ref(), &mut char_limbs); - char_limbs.reverse(); - - for _ in 0..1000 { - // Exponentiating by the modulus should have no effect in a prime field. - let a = Fq::random(&mut rng); - - assert_eq!(a, a.pow_vartime(char_limbs)); - } -} - -#[test] -fn test_fq_sqrt() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fq::random(&mut rng); - let nega = a.neg(); - let b = a.square(); - - let b = b.sqrt().unwrap(); - - assert!(a == b || nega == b); - } - - for _ in 0..1000 { - // Ensure sqrt(a)^2 = a for random a - let a = Fq::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[test] -fn test_fq_from_to_repr() { - // q + 1 should not be in the field - assert!(Fq::from_repr(FqRepr([ - 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, - 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, - 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xaa, 0xac, - ])) - .is_none()); - - // q should not be in the field - assert!(Fq::from_repr(Fq::char()).is_none()); - - // Multiply some arbitrary representations to see if the result is as expected. - let a = FqRepr([ - 0x06, 0xc8, 0x09, 0x18, 0xa3, 0x65, 0xef, 0x78, 0xae, 0x30, 0x74, 0x0c, 0x08, 0xa8, 0x75, - 0xd7, 0x90, 0x8a, 0x38, 0x7f, 0x48, 0x07, 0x35, 0xf1, 0x2b, 0x1f, 0x41, 0xab, 0x9f, 0x36, - 0xd6, 0x40, 0xac, 0x62, 0xa8, 0x2a, 0x8f, 0x51, 0xcd, 0x50, 0x4a, 0x49, 0xda, 0xd4, 0xff, - 0x6c, 0xde, 0x2d, - ]); - let mut a_fq = Fq::from_repr(a).unwrap(); - let b = FqRepr([ - 0x0b, 0x13, 0x95, 0x5f, 0x5a, 0xc7, 0xf6, 0xa3, 0x1f, 0x60, 0x71, 0x86, 0xd5, 0xbb, 0x00, - 0x59, 0xd5, 0x9f, 0xd9, 0x4e, 0xe4, 0x57, 0x2c, 0xfa, 0x94, 0x98, 0xb4, 0x29, 0x2f, 0xd2, - 0x74, 0x59, 0xe7, 0xf8, 0x78, 0xcf, 0x87, 0xf0, 0x5e, 0x5d, 0xbb, 0xa5, 0x79, 0x17, 0xc3, - 0x2f, 0x0c, 0xf0, - ]); - let b_fq = Fq::from_repr(b).unwrap(); - let c = FqRepr([ - 0x16, 0x84, 0x48, 0x77, 0x72, 0xbc, 0x9a, 0x5a, 0xa4, 0x4c, 0x20, 0x4c, 0x1d, 0xe7, 0xcd, - 0xb7, 0xf1, 0x6b, 0x9d, 0x77, 0xb0, 0xad, 0x7d, 0x10, 0xce, 0x60, 0xdd, 0x43, 0x41, 0x7e, - 0xc9, 0x60, 0x35, 0x5e, 0xa5, 0xac, 0x64, 0xcb, 0xba, 0xb1, 0xf5, 0xf7, 0x07, 0x13, 0xb7, - 0x17, 0x91, 0x4c, - ]); - a_fq.mul_assign(&b_fq); - assert_eq!(a_fq.to_repr(), c); - - // Zero should be in the field. - assert!(Fq::from_repr(FqRepr([0; 48])).unwrap().is_zero()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fq elements into representations and back again, and compare. - let a = Fq::random(&mut rng); - let a_repr = a.to_repr(); - let b_repr = FqRepr::from(a); - assert_eq!(a_repr, b_repr); - let a_again = Fq::from_repr(a_repr).unwrap(); - - assert_eq!(a, a_again); - } -} - -#[test] -fn test_fq_display() { - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([ - 0x19, 0x47, 0xf0, 0xd5, 0xf4, 0xfe, 0x32, 0x5a, 0xb1, 0xd4, 0xaa, 0xd8, 0x76, 0x51, - 0xe6, 0x94, 0x67, 0x6c, 0xc4, 0xee, 0xf4, 0xc4, 0x6f, 0x2c, 0xb3, 0x8d, 0x35, 0xb3, - 0xf6, 0x77, 0x95, 0x85, 0x39, 0xa8, 0xf1, 0x84, 0xf3, 0x53, 0x5c, 0x7b, 0xa9, 0x56, - 0xba, 0xbf, 0x93, 0x01, 0xea, 0x24, - ])).unwrap()), - "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() - ); - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([ - 0x06, 0xc9, 0xf5, 0xa1, 0x06, 0x0d, 0xe9, 0x74, 0x9a, 0x55, 0x18, 0x59, 0xb1, 0xe4, - 0x3a, 0x9a, 0xb7, 0xf8, 0x9f, 0x88, 0xf5, 0x9c, 0x1d, 0xc5, 0xa4, 0xb6, 0x2a, 0xf4, - 0xa7, 0x92, 0xa6, 0x89, 0x41, 0x3f, 0x6f, 0x7f, 0x06, 0xea, 0x87, 0xeb, 0xe2, 0x8e, - 0x79, 0x39, 0x6a, 0xc2, 0xbb, 0xf8, - ])).unwrap()), - "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() - ); -} - -#[test] -fn test_fq_is_odd() { - assert!(!Fq::from(0).is_odd()); - assert!(Fq::from(0).is_even()); - assert!(Fq::from(1).is_odd()); - assert!(!Fq::from(1).is_even()); - assert!(!Fq::from(324834872).is_odd()); - assert!(Fq::from(324834872).is_even()); - assert!(Fq::from(324834873).is_odd()); - assert!(!Fq::from(324834873).is_even()); -} - -#[test] -fn test_fq_num_bits() { - assert_eq!(Fq::NUM_BITS, 381); - assert_eq!(Fq::CAPACITY, 380); -} - -#[test] -fn test_fq_root_of_unity() { - assert_eq!(Fq::S, 1); - assert_eq!(Fq::multiplicative_generator(), Fq::from(2)); - assert_eq!( - Fq::multiplicative_generator().pow_vartime([ - 0xdcff7fffffffd555u64, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d - ]), - Fq::root_of_unity() - ); - assert_eq!(Fq::root_of_unity().pow_vartime([1u64 << Fq::S]), Fq::one()); - assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none())); -} - -#[test] -fn fq_field_tests() { - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); - crate::tests::field::from_str_tests::(); -} - -#[test] -fn test_fq_ordering() { - // We need to make sure the Fq elements aren't being compared in Montgomery form. - for i in 0..100 { - assert!(Fq::from(i + 1) > Fq::from(i)); - } -} - -#[test] -fn fq_repr_tests() { - crate::tests::repr::random_repr_tests::(); -} diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs deleted file mode 100644 index 56632cbd4f..0000000000 --- a/pairing/src/bls12_381/fq12.rs +++ /dev/null @@ -1,286 +0,0 @@ -use super::fq::FROBENIUS_COEFF_FQ12_C1; -use super::fq2::Fq2; -use super::fq6::Fq6; -use ff::Field; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq12, represented by c0 + c1 * w. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Fq12 { - pub c0: Fq6, - pub c1: Fq6, -} - -impl ::std::fmt::Display for Fq12 { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fq12({} + {} * w)", self.c0, self.c1) - } -} - -impl Fq12 { - pub fn conjugate(&mut self) { - self.c1 = self.c1.neg(); - } - - pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { - let mut aa = self.c0; - aa.mul_by_01(c0, c1); - let mut bb = self.c1; - bb.mul_by_1(c4); - let mut o = *c1; - o.add_assign(c4); - self.c1.add_assign(&self.c0); - self.c1.mul_by_01(c0, &o); - self.c1.sub_assign(&aa); - self.c1.sub_assign(&bb); - self.c0 = bb; - self.c0.mul_by_nonresidue(); - self.c0.add_assign(&aa); - } - - pub fn frobenius_map(&mut self, power: usize) { - self.c0.frobenius_map(power); - self.c1.frobenius_map(power); - - self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - } -} - -impl ConditionallySelectable for Fq12 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq12 { - c0: Fq6::conditional_select(&a.c0, &b.c0, choice), - c1: Fq6::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl Neg for Fq12 { - type Output = Self; - - fn neg(self) -> Self { - Fq12 { - c0: self.c0.neg(), - c1: self.c1.neg(), - } - } -} - -impl<'r> Add<&'r Fq12> for Fq12 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq12 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - } - } -} - -impl Add for Fq12 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq12> for Fq12 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } -} - -impl AddAssign for Fq12 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq12> for Fq12 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq12 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - } - } -} - -impl Sub for Fq12 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq12> for Fq12 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } -} - -impl SubAssign for Fq12 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq12> for Fq12 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq12 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq12> for Fq12 { - fn mul_assign(&mut self, other: &Self) { - let mut aa = self.c0; - aa.mul_assign(&other.c0); - let mut bb = self.c1; - bb.mul_assign(&other.c1); - let mut o = other.c0; - o.add_assign(&other.c1); - self.c1.add_assign(&self.c0); - self.c1.mul_assign(&o); - self.c1.sub_assign(&aa); - self.c1.sub_assign(&bb); - self.c0 = bb; - self.c0.mul_by_nonresidue(); - self.c0.add_assign(&aa); - } -} - -impl MulAssign for Fq12 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq12 { - fn random(rng: &mut R) -> Self { - Fq12 { - c0: Fq6::random(rng), - c1: Fq6::random(rng), - } - } - - fn zero() -> Self { - Fq12 { - c0: Fq6::zero(), - c1: Fq6::zero(), - } - } - - fn one() -> Self { - Fq12 { - c0: Fq6::one(), - c1: Fq6::zero(), - } - } - - fn is_zero(&self) -> bool { - self.c0.is_zero() && self.c1.is_zero() - } - - fn double(&self) -> Self { - Fq12 { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - fn square(&self) -> Self { - let mut ab = self.c0; - ab.mul_assign(&self.c1); - let mut c0c1 = self.c0; - c0c1.add_assign(&self.c1); - let mut c0 = self.c1; - c0.mul_by_nonresidue(); - c0.add_assign(&self.c0); - c0.mul_assign(&c0c1); - c0.sub_assign(&ab); - let mut c1 = ab; - c1.add_assign(&ab); - ab.mul_by_nonresidue(); - c0.sub_assign(&ab); - Fq12 { c0, c1 } - } - - fn invert(&self) -> CtOption { - let mut c0s = self.c0.square(); - let mut c1s = self.c1.square(); - c1s.mul_by_nonresidue(); - c0s.sub_assign(&c1s); - - c0s.invert().map(|t| Fq12 { - c0: t.mul(&self.c0), - c1: t.mul(&self.c1).neg(), - }) - } - - fn sqrt(&self) -> CtOption { - unimplemented!() - } -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq12_mul_by_014() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c0 = Fq2::random(&mut rng); - let c1 = Fq2::random(&mut rng); - let c5 = Fq2::random(&mut rng); - let mut a = Fq12::random(&mut rng); - let mut b = a; - - a.mul_by_014(&c0, &c1, &c5); - b.mul_assign(&Fq12 { - c0: Fq6 { - c0, - c1, - c2: Fq2::zero(), - }, - c1: Fq6 { - c0: Fq2::zero(), - c1: c5, - c2: Fq2::zero(), - }, - }); - - assert_eq!(a, b); - } -} - -#[test] -fn fq12_field_tests() { - crate::tests::field::random_field_tests::(); -} diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs deleted file mode 100644 index 335664d584..0000000000 --- a/pairing/src/bls12_381/fq2.rs +++ /dev/null @@ -1,925 +0,0 @@ -use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::Field; -use rand_core::RngCore; -use std::cmp::Ordering; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq2, represented by c0 + c1 * u. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Fq2 { - pub c0: Fq, - pub c1: Fq, -} - -impl ::std::fmt::Display for Fq2 { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fq2({} + {} * u)", self.c0, self.c1) - } -} - -/// `Fq2` elements are ordered lexicographically. -impl Ord for Fq2 { - #[inline(always)] - fn cmp(&self, other: &Fq2) -> Ordering { - match self.c1.cmp(&other.c1) { - Ordering::Greater => Ordering::Greater, - Ordering::Less => Ordering::Less, - Ordering::Equal => self.c0.cmp(&other.c0), - } - } -} - -impl PartialOrd for Fq2 { - #[inline(always)] - fn partial_cmp(&self, other: &Fq2) -> Option { - Some(self.cmp(other)) - } -} - -impl Fq2 { - /// Multiply this element by the cubic and quadratic nonresidue 1 + u. - pub fn mul_by_nonresidue(&mut self) { - let t0 = self.c0; - self.c0.sub_assign(&self.c1); - self.c1.add_assign(&t0); - } - - /// Norm of Fq2 as extension field in i over Fq - pub fn norm(&self) -> Fq { - let t0 = self.c0.square(); - let mut t1 = self.c1.square(); - t1.add_assign(&t0); - - t1 - } - - pub fn frobenius_map(&mut self, power: usize) { - self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); - } -} - -impl ConditionallySelectable for Fq2 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq2 { - c0: Fq::conditional_select(&a.c0, &b.c0, choice), - c1: Fq::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl Neg for Fq2 { - type Output = Self; - - fn neg(self) -> Self { - Fq2 { - c0: self.c0.neg(), - c1: self.c1.neg(), - } - } -} - -impl<'r> Add<&'r Fq2> for Fq2 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq2 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - } - } -} - -impl Add for Fq2 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq2> for Fq2 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } -} - -impl AddAssign for Fq2 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq2> for Fq2 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq2 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - } - } -} - -impl Sub for Fq2 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq2> for Fq2 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } -} - -impl SubAssign for Fq2 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq2> for Fq2 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq2 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq2> for Fq2 { - fn mul_assign(&mut self, other: &Self) { - let mut aa = self.c0; - aa.mul_assign(&other.c0); - let mut bb = self.c1; - bb.mul_assign(&other.c1); - let mut o = other.c0; - o.add_assign(&other.c1); - self.c1.add_assign(&self.c0); - self.c1.mul_assign(&o); - self.c1.sub_assign(&aa); - self.c1.sub_assign(&bb); - self.c0 = aa; - self.c0.sub_assign(&bb); - } -} - -impl MulAssign for Fq2 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq2 { - fn random(rng: &mut R) -> Self { - Fq2 { - c0: Fq::random(rng), - c1: Fq::random(rng), - } - } - - fn zero() -> Self { - Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - } - } - - fn one() -> Self { - Fq2 { - c0: Fq::one(), - c1: Fq::zero(), - } - } - - fn is_zero(&self) -> bool { - self.c0.is_zero() && self.c1.is_zero() - } - - fn square(&self) -> Self { - let mut ab = self.c0; - ab.mul_assign(&self.c1); - let mut c0c1 = self.c0; - c0c1.add_assign(&self.c1); - let mut c0 = self.c1.neg(); - c0.add_assign(&self.c0); - c0.mul_assign(&c0c1); - c0.sub_assign(&ab); - let mut c1 = ab; - c1.add_assign(&ab); - c0.add_assign(&ab); - Fq2 { c0, c1 } - } - - fn double(&self) -> Self { - Fq2 { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - fn invert(&self) -> CtOption { - let t1 = self.c1.square(); - let mut t0 = self.c0.square(); - t0.add_assign(&t1); - t0.invert().map(|t| Fq2 { - c0: self.c0.mul(&t), - c1: self.c1.mul(&t).neg(), - }) - } - - /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! - /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! - fn sqrt(&self) -> CtOption { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - - if self.is_zero() { - CtOption::new(Self::zero(), Choice::from(1)) - } else { - // a1 = self^((q - 3) / 4) - let mut a1 = self.pow_vartime([ - 0xee7fbfffffffeaaau64, - 0x7aaffffac54ffff, - 0xd9cc34a83dac3d89, - 0xd91dd2e13ce144af, - 0x92c6e9ed90d2eb35, - 0x680447a8e5ff9a6, - ]); - let mut alpha = a1.square(); - alpha.mul_assign(self); - let mut a0 = alpha; - a0.frobenius_map(1); - a0.mul_assign(&alpha); - - let neg1 = Fq2 { - c0: NEGATIVE_ONE, - c1: Fq::zero(), - }; - - if a0 == neg1 { - CtOption::new(Self::zero(), Choice::from(0)) - } else { - a1.mul_assign(self); - - if alpha == neg1 { - a1.mul_assign(&Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }); - } else { - alpha.add_assign(&Fq2::one()); - // alpha = alpha^((q - 1) / 2) - alpha = alpha.pow_vartime([ - 0xdcff7fffffffd555u64, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d, - ]); - a1.mul_assign(&alpha); - } - - CtOption::new(a1, Choice::from(1)) - } - } - } -} - -#[cfg(test)] -use super::fq::FqRepr; -#[cfg(test)] -use ff::PrimeField; - -#[test] -fn test_fq2_ordering() { - let mut a = Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }; - - let mut b = a; - - assert!(a.cmp(&b) == Ordering::Equal); - b.c0.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Less); - a.c0.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Equal); - b.c1.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Less); - a.c0.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Less); - a.c1.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Greater); - b.c0.add_assign(&Fq::one()); - assert!(a.cmp(&b) == Ordering::Equal); -} - -#[test] -fn test_fq2_basics() { - assert_eq!( - Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }, - Fq2::zero() - ); - assert_eq!( - Fq2 { - c0: Fq::one(), - c1: Fq::zero(), - }, - Fq2::one() - ); - assert!(Fq2::zero().is_zero()); - assert!(!Fq2::one().is_zero()); - assert!(!Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - } - .is_zero()); -} - -#[test] -fn test_fq2_squaring() { - let a = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; // u + 1 - assert_eq!( - a.square(), - Fq2 { - c0: Fq::zero(), - c1: Fq::from(2), - } - ); // 2u - - let a = Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }; // u - assert_eq!(a.square(), { - Fq2 { - c0: Fq::one().neg(), - c1: Fq::zero(), - } - }); // -1 - - let a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x07, 0x08, 0x0c, 0x5f, 0xa1, 0xd8, 0xe0, 0x42, 0x41, 0xb7, 0x6d, 0xcc, 0x1c, 0x3f, - 0xbe, 0x5e, 0xf7, 0xf2, 0x95, 0xa9, 0x4e, 0x58, 0xae, 0x7c, 0x90, 0xe3, 0x4a, 0xab, - 0x6f, 0xb6, 0xa6, 0xbd, 0x4e, 0xef, 0x5c, 0x94, 0x65, 0x36, 0xf6, 0x02, 0x9c, 0x2c, - 0x63, 0x09, 0xbb, 0xf8, 0xb5, 0x98, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x10, 0xd1, 0x61, 0x5e, 0x75, 0x25, 0x0a, 0x21, 0xfc, 0x58, 0xa7, 0xb7, 0xbe, 0x81, - 0x54, 0x07, 0xbf, 0xb9, 0x90, 0x20, 0x60, 0x41, 0x37, 0xa0, 0xda, 0xc5, 0xa4, 0xc9, - 0x11, 0xa4, 0x35, 0x3e, 0x6a, 0xd3, 0x29, 0x11, 0x77, 0xc8, 0xc7, 0xe5, 0x38, 0xf4, - 0x73, 0xb3, 0xc8, 0x70, 0xa4, 0xab, - ])) - .unwrap(), - }; - assert_eq!( - a.square(), - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x07, 0xea, 0xc8, 0x13, 0x69, 0xc4, 0x33, 0x61, 0x4c, 0xf1, 0x7b, 0x58, 0x93, 0xc3, - 0xd3, 0x27, 0xcb, 0x67, 0x41, 0x57, 0x61, 0x8d, 0xa1, 0x76, 0x0d, 0xc4, 0x6a, 0xb8, - 0xfa, 0xd6, 0x7a, 0xe0, 0xb9, 0xf2, 0xa6, 0x6e, 0xae, 0x10, 0x73, 0xba, 0xf2, 0x62, - 0xc2, 0x8c, 0x53, 0x8b, 0xcf, 0x68, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x15, 0x42, 0xa6, 0x1c, 0x8a, 0x8d, 0xb9, 0x94, 0x73, 0x9c, 0x98, 0x30, 0x42, 0x77, - 0x9a, 0x65, 0x38, 0xd0, 0xd7, 0x27, 0x5a, 0x96, 0x89, 0xe1, 0xe7, 0x51, 0x38, 0xbc, - 0xe4, 0xce, 0xc7, 0xaa, 0xa2, 0x3e, 0xb7, 0xe1, 0x2d, 0xd5, 0x4d, 0x98, 0xc1, 0x57, - 0x9c, 0xf5, 0x8e, 0x98, 0x0c, 0xf8, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_mul() { - use super::fq::FqRepr; - use ff::PrimeField; - - let mut a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, - 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, - 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, - 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, - 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, - 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, - 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, - ])) - .unwrap(), - }; - a.mul_assign(&Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x02, 0xc9, 0x3a, 0x72, 0xeb, 0x8a, 0xf8, 0x3e, 0x06, 0xc9, 0x11, 0x02, 0x92, 0xbf, - 0xa4, 0x09, 0xcd, 0x46, 0x0f, 0x9f, 0x0c, 0x23, 0xe4, 0x30, 0x27, 0xec, 0xe1, 0x75, - 0xbe, 0x07, 0xa5, 0x31, 0xfc, 0x87, 0xe6, 0x2e, 0x17, 0x9c, 0x28, 0x5d, 0xe2, 0x1f, - 0x91, 0x69, 0x80, 0x5f, 0x53, 0x7e, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x19, 0xe1, 0x73, 0x34, 0xd4, 0xe9, 0x35, 0x58, 0x63, 0x4c, 0xd3, 0xc6, 0xc5, 0x65, - 0x09, 0x6d, 0x57, 0xa0, 0x6d, 0x31, 0x35, 0xa7, 0x52, 0xae, 0x88, 0x71, 0xc5, 0x08, - 0x65, 0x8d, 0x1e, 0x5f, 0x1d, 0x2a, 0x72, 0x91, 0x6d, 0xba, 0x4c, 0x8a, 0x4b, 0x1c, - 0x3f, 0x93, 0x6d, 0x89, 0x92, 0xd4, - ])) - .unwrap(), - }); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x17, 0x51, 0xaf, 0xbe, 0x16, 0x6e, 0x53, 0x99, 0x53, 0x10, 0xa2, 0x02, 0xd9, 0x2f, - 0x99, 0x63, 0x55, 0x11, 0xfe, 0x4d, 0x84, 0xee, 0x5f, 0x78, 0xf6, 0x1a, 0x96, 0xda, - 0xcf, 0x5a, 0x39, 0xbc, 0xde, 0x29, 0xc3, 0x1a, 0x19, 0xa6, 0x93, 0x7e, 0x95, 0xb5, - 0x12, 0x7e, 0x63, 0x60, 0xc7, 0xe4, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0xef, 0x1a, 0x36, 0xc2, 0x01, 0x58, 0x9d, 0x33, 0xa9, 0xac, 0x82, 0xce, 0x4c, - 0x50, 0x83, 0xc9, 0x75, 0x10, 0x65, 0x79, 0xc2, 0x75, 0xee, 0x5b, 0xa6, 0xe5, 0x43, - 0x0e, 0x88, 0x3d, 0x40, 0x06, 0xc6, 0x3c, 0xd4, 0xda, 0x2c, 0x2a, 0xa7, 0x84, 0xaf, - 0x0e, 0x1b, 0xd6, 0x30, 0x11, 0x7a, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_invert() { - use super::fq::FqRepr; - use ff::PrimeField; - - assert!(bool::from(Fq2::zero().invert().is_none())); - - let a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, - 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, - 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, - 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, - 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, - 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, - 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, - ])) - .unwrap(), - }; - let a = a.invert().unwrap(); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x13, 0x51, 0xef, 0x01, 0x94, 0x1b, 0x70, 0xc4, 0xa6, 0xc3, 0xd8, 0xf9, 0x58, 0x6f, - 0x26, 0x36, 0xdf, 0xba, 0x70, 0x32, 0x93, 0x94, 0x1c, 0x30, 0x64, 0xbe, 0xf6, 0x17, - 0xd2, 0x91, 0x5a, 0x8f, 0xe5, 0xec, 0xda, 0x5f, 0xda, 0xfd, 0xdb, 0xb2, 0x07, 0x03, - 0x00, 0xf9, 0xbc, 0xb9, 0xe5, 0x94, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x10, 0x3b, 0xdf, 0x24, 0x1a, 0xfb, 0x00, 0x19, 0xdf, 0x4e, 0x54, 0xf0, 0xd3, 0xef, - 0x15, 0xa6, 0xcb, 0xf6, 0x51, 0xa0, 0xf3, 0x67, 0xaf, 0xb2, 0x94, 0x71, 0x43, 0xf8, - 0x9f, 0xae, 0xde, 0xe9, 0x15, 0xd7, 0xb6, 0xb9, 0x5d, 0xef, 0xbf, 0xf0, 0x8c, 0x39, - 0xfd, 0x76, 0xa8, 0x31, 0x2c, 0xb4, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_addition() { - use super::fq::FqRepr; - use ff::PrimeField; - - let mut a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - }; - a.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, - 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, - 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, - 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, - 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, - 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, - 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, - ])) - .unwrap(), - }); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x14, 0xc7, 0x15, 0xd5, 0x55, 0x3f, 0x01, 0xd2, 0x65, 0x30, 0x94, 0x27, 0xb3, 0xd5, - 0xd0, 0x90, 0xf4, 0xef, 0x57, 0xd6, 0x04, 0xb6, 0xbc, 0xa2, 0xd7, 0x0b, 0x0c, 0x7b, - 0x48, 0x1d, 0x23, 0xff, 0xcb, 0x20, 0x7e, 0x6b, 0x33, 0x41, 0xea, 0xba, 0x8e, 0x9a, - 0x7a, 0xda, 0xf6, 0xeb, 0x0e, 0xb9, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x13, 0x03, 0xf3, 0x46, 0x51, 0x12, 0xc8, 0xbc, 0x9a, 0xd2, 0x65, 0xeb, 0x46, 0xe0, - 0x19, 0x84, 0xd6, 0x2f, 0xa5, 0x13, 0x34, 0xf5, 0x60, 0xfa, 0xfe, 0x4b, 0x23, 0x31, - 0x7e, 0x07, 0x96, 0xd5, 0x35, 0xa2, 0x80, 0x9d, 0x15, 0x46, 0x8d, 0x83, 0xfd, 0xb0, - 0x32, 0xe7, 0xd9, 0x07, 0x9a, 0x94, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_subtraction() { - use super::fq::FqRepr; - use ff::PrimeField; - - let mut a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - }; - a.sub_assign(&Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, - 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, - 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, - 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, - 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, - 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, - 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, - ])) - .unwrap(), - }); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x07, 0x2b, 0xa1, 0x40, 0x49, 0xfe, 0x98, 0x81, 0x7f, 0x77, 0xa7, 0x18, 0x02, 0x1c, - 0x34, 0x2d, 0xe2, 0x55, 0x90, 0x26, 0x72, 0xef, 0x6c, 0x43, 0xa6, 0x5a, 0x6b, 0xe7, - 0x00, 0xb2, 0x85, 0xfe, 0x77, 0x56, 0xbe, 0xd7, 0xc1, 0x59, 0x82, 0xe9, 0x85, 0x65, - 0x75, 0x2b, 0xdb, 0x5c, 0x9b, 0x80, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x09, 0x57, 0x41, 0x13, 0x59, 0xba, 0x6e, 0x4c, 0x4c, 0xd5, 0xdd, 0x9f, 0xb4, 0x0b, - 0x3b, 0x8f, 0xf6, 0x39, 0x05, 0xf3, 0x9a, 0xd8, 0xcb, 0x1f, 0xe5, 0x26, 0x17, 0x93, - 0x05, 0x88, 0xc6, 0x9a, 0x11, 0xdf, 0x49, 0xbc, 0x6c, 0xac, 0xc2, 0x56, 0xeb, 0x4a, - 0xba, 0xf7, 0xc2, 0x55, 0xd1, 0xcd, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_negation() { - use super::fq::FqRepr; - use ff::PrimeField; - - let a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - } - .neg(); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x19, 0x08, 0x3f, 0x54, 0x86, 0xa1, 0x0c, 0xbd, 0x7e, 0x55, 0x5d, 0xf1, 0x89, 0xf8, - 0x80, 0xe3, 0xab, 0x10, 0x7d, 0x49, 0x31, 0x74, 0x87, 0xab, 0xdc, 0x16, 0x7f, 0xc0, - 0x4d, 0xa1, 0x9c, 0x37, 0x0c, 0xc6, 0x61, 0x5c, 0x8f, 0xb0, 0x49, 0x2d, 0x8c, 0xfe, - 0x87, 0xfc, 0x96, 0xdb, 0xaa, 0xe4, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, - 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, - 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, - 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_doubling() { - use super::fq::FqRepr; - use ff::PrimeField; - - let a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - }; - assert_eq!( - a.double(), - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x01, 0xf1, 0xa5, 0x2b, 0x65, 0xbd, 0xb3, 0xb9, 0x99, 0x8c, 0x93, 0x89, 0x72, 0xa6, - 0x57, 0xe7, 0x72, 0xcd, 0x9c, 0x77, 0x84, 0x21, 0x16, 0x27, 0x16, 0x34, 0xa5, 0xc1, - 0x52, 0x1e, 0xb3, 0xda, 0x23, 0xcb, 0x3d, 0x44, 0x43, 0x47, 0x6d, 0xa4, 0x5a, 0x00, - 0xf0, 0x06, 0xd2, 0x47, 0xff, 0x8e, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x02, 0x5a, 0x22, 0x6f, 0x71, 0x4d, 0x50, 0x6e, 0x9c, 0x8c, 0x9b, 0xd4, 0xb7, 0x9f, - 0xa8, 0x3d, 0x67, 0xf1, 0x5f, 0x81, 0xdc, 0x49, 0x19, 0x5b, 0x7c, 0x40, 0x68, 0x23, - 0x8c, 0xdf, 0x67, 0x4b, 0x28, 0xd5, 0xca, 0x5a, 0xd0, 0x9f, 0x4f, 0xdb, 0x2e, 0xfb, - 0xed, 0xdf, 0x9b, 0x5d, 0xc1, 0xb6, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_frobenius_map() { - use super::fq::FqRepr; - use ff::PrimeField; - - let mut a = Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - }; - a.frobenius_map(0); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - } - ); - a.frobenius_map(1); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, - 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, - 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, - 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, - ])) - .unwrap(), - } - ); - a.frobenius_map(1); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - } - ); - a.frobenius_map(2); - assert_eq!( - a, - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, - 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, - 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, - 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, - 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, - 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, - 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, - ])) - .unwrap(), - } - ); -} - -#[test] -fn test_fq2_sqrt() { - use super::fq::FqRepr; - use ff::PrimeField; - - assert_eq!( - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x07, 0xca, 0x7d, 0xa1, 0xf1, 0x36, 0x06, 0xac, 0x1e, 0x58, 0xb2, 0x15, 0x9d, 0xfe, - 0x10, 0xe2, 0xdb, 0x4a, 0x11, 0x6b, 0x5b, 0xf7, 0x4a, 0xa1, 0xa5, 0x7e, 0x6f, 0xc1, - 0xba, 0xb5, 0x1f, 0xd9, 0x03, 0x4c, 0x2d, 0x04, 0xfa, 0xff, 0xda, 0xb6, 0x47, 0x6b, - 0x4c, 0x30, 0x97, 0x20, 0xe2, 0x27, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x0e, 0xc9, 0x23, 0x36, 0x65, 0x0e, 0x49, 0xd5, 0x08, 0xee, 0x53, 0x94, 0xd7, 0x7a, - 0xfb, 0x3d, 0x21, 0x26, 0x11, 0xbc, 0xa4, 0xe9, 0x91, 0x21, 0x4c, 0xec, 0x2d, 0xca, - 0x57, 0x7a, 0x3e, 0xb6, 0x37, 0x1a, 0x75, 0xed, 0x14, 0xf4, 0x16, 0x29, 0xfa, 0x8d, - 0xe8, 0x8b, 0x75, 0x16, 0xd2, 0xc3, - ])) - .unwrap(), - } - .sqrt() - .unwrap(), - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x10, 0xf6, 0x96, 0x3b, 0xba, 0xd2, 0xeb, 0xc5, 0x88, 0x1b, 0x3e, 0x01, 0xb6, 0x11, - 0xc0, 0x70, 0x8d, 0x7f, 0x1f, 0x72, 0x3d, 0x02, 0xc1, 0xd3, 0x6d, 0x2d, 0xdb, 0xe5, - 0x52, 0x20, 0x3e, 0x82, 0x6e, 0xf7, 0xde, 0x92, 0xe8, 0xc6, 0x8b, 0x63, 0x40, 0xb2, - 0x99, 0xb2, 0x70, 0x42, 0x58, 0xc5, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x08, 0xd7, 0xcf, 0xff, 0x94, 0x21, 0x63, 0x30, 0xa4, 0xc9, 0x3b, 0x08, 0x10, 0x5d, - 0x71, 0xa9, 0x6b, 0x85, 0x2a, 0xea, 0xf2, 0xaf, 0xcb, 0x1b, 0x28, 0xa2, 0x0f, 0xae, - 0xd2, 0x11, 0xef, 0xe7, 0x76, 0x70, 0x59, 0x46, 0x65, 0x67, 0x64, 0x47, 0xc0, 0x99, - 0x53, 0x4f, 0xc2, 0x09, 0xe7, 0x52, - ])) - .unwrap(), - } - ); - - assert_eq!( - Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, - 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, - 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xf7, - 0x84, 0x29, 0xd1, 0x51, 0x7a, 0x6b, - ])) - .unwrap(), - c1: Fq::zero(), - } - .sqrt() - .unwrap(), - Fq2 { - c0: Fq::zero(), - c1: Fq::from_repr(FqRepr([ - 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, - 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, - 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, - 0xff, 0xff, 0xfd, 0x43, 0x57, 0xa3, - ])) - .unwrap(), - } - ); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq2_mul_nonresidue() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let nqr = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - for _ in 0..1000 { - let mut a = Fq2::random(&mut rng); - let mut b = a; - a.mul_by_nonresidue(); - b.mul_assign(&nqr); - - assert_eq!(a, b); - } -} - -#[test] -fn fq2_field_tests() { - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); -} diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs deleted file mode 100644 index 52f7aaab42..0000000000 --- a/pairing/src/bls12_381/fq6.rs +++ /dev/null @@ -1,478 +0,0 @@ -use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; -use super::fq2::Fq2; -use ff::Field; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Fq6 { - pub c0: Fq2, - pub c1: Fq2, - pub c2: Fq2, -} - -impl ::std::fmt::Display for Fq6 { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) - } -} - -impl Fq6 { - /// Multiply by quadratic nonresidue v. - pub fn mul_by_nonresidue(&mut self) { - use std::mem::swap; - swap(&mut self.c0, &mut self.c1); - swap(&mut self.c0, &mut self.c2); - - self.c0.mul_by_nonresidue(); - } - - pub fn mul_by_1(&mut self, c1: &Fq2) { - let mut b_b = self.c1; - b_b.mul_assign(c1); - - let mut t1 = *c1; - { - let mut tmp = self.c1; - tmp.add_assign(&self.c2); - - t1.mul_assign(&tmp); - t1.sub_assign(&b_b); - t1.mul_by_nonresidue(); - } - - let mut t2 = *c1; - { - let mut tmp = self.c0; - tmp.add_assign(&self.c1); - - t2.mul_assign(&tmp); - t2.sub_assign(&b_b); - } - - self.c0 = t1; - self.c1 = t2; - self.c2 = b_b; - } - - pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) { - let mut a_a = self.c0; - let mut b_b = self.c1; - a_a.mul_assign(c0); - b_b.mul_assign(c1); - - let mut t1 = *c1; - { - let mut tmp = self.c1; - tmp.add_assign(&self.c2); - - t1.mul_assign(&tmp); - t1.sub_assign(&b_b); - t1.mul_by_nonresidue(); - t1.add_assign(&a_a); - } - - let mut t3 = *c0; - { - let mut tmp = self.c0; - tmp.add_assign(&self.c2); - - t3.mul_assign(&tmp); - t3.sub_assign(&a_a); - t3.add_assign(&b_b); - } - - let mut t2 = *c0; - t2.add_assign(c1); - { - let mut tmp = self.c0; - tmp.add_assign(&self.c1); - - t2.mul_assign(&tmp); - t2.sub_assign(&a_a); - t2.sub_assign(&b_b); - } - - self.c0 = t1; - self.c1 = t2; - self.c2 = t3; - } - - pub fn frobenius_map(&mut self, power: usize) { - self.c0.frobenius_map(power); - self.c1.frobenius_map(power); - self.c2.frobenius_map(power); - - self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); - self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); - } -} - -impl ConditionallySelectable for Fq6 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq6 { - c0: Fq2::conditional_select(&a.c0, &b.c0, choice), - c1: Fq2::conditional_select(&a.c1, &b.c1, choice), - c2: Fq2::conditional_select(&a.c2, &b.c2, choice), - } - } -} - -impl Neg for Fq6 { - type Output = Self; - - fn neg(self) -> Self { - Fq6 { - c0: self.c0.neg(), - c1: self.c1.neg(), - c2: self.c2.neg(), - } - } -} - -impl<'r> Add<&'r Fq6> for Fq6 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq6 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - c2: self.c2 + other.c2, - } - } -} - -impl Add for Fq6 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq6> for Fq6 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - self.c2.add_assign(&other.c2); - } -} - -impl AddAssign for Fq6 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq6> for Fq6 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq6 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - c2: self.c2 - other.c2, - } - } -} - -impl Sub for Fq6 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq6> for Fq6 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - self.c2.sub_assign(&other.c2); - } -} - -impl SubAssign for Fq6 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq6> for Fq6 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq6 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq6> for Fq6 { - fn mul_assign(&mut self, other: &Self) { - let mut a_a = self.c0; - let mut b_b = self.c1; - let mut c_c = self.c2; - a_a.mul_assign(&other.c0); - b_b.mul_assign(&other.c1); - c_c.mul_assign(&other.c2); - - let mut t1 = other.c1; - t1.add_assign(&other.c2); - { - let mut tmp = self.c1; - tmp.add_assign(&self.c2); - - t1.mul_assign(&tmp); - t1.sub_assign(&b_b); - t1.sub_assign(&c_c); - t1.mul_by_nonresidue(); - t1.add_assign(&a_a); - } - - let mut t3 = other.c0; - t3.add_assign(&other.c2); - { - let mut tmp = self.c0; - tmp.add_assign(&self.c2); - - t3.mul_assign(&tmp); - t3.sub_assign(&a_a); - t3.add_assign(&b_b); - t3.sub_assign(&c_c); - } - - let mut t2 = other.c0; - t2.add_assign(&other.c1); - { - let mut tmp = self.c0; - tmp.add_assign(&self.c1); - - t2.mul_assign(&tmp); - t2.sub_assign(&a_a); - t2.sub_assign(&b_b); - c_c.mul_by_nonresidue(); - t2.add_assign(&c_c); - } - - self.c0 = t1; - self.c1 = t2; - self.c2 = t3; - } -} - -impl MulAssign for Fq6 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq6 { - fn random(rng: &mut R) -> Self { - Fq6 { - c0: Fq2::random(rng), - c1: Fq2::random(rng), - c2: Fq2::random(rng), - } - } - - fn zero() -> Self { - Fq6 { - c0: Fq2::zero(), - c1: Fq2::zero(), - c2: Fq2::zero(), - } - } - - fn one() -> Self { - Fq6 { - c0: Fq2::one(), - c1: Fq2::zero(), - c2: Fq2::zero(), - } - } - - fn is_zero(&self) -> bool { - self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() - } - - fn double(&self) -> Self { - Fq6 { - c0: self.c0.double(), - c1: self.c1.double(), - c2: self.c2.double(), - } - } - - fn square(&self) -> Self { - let s0 = self.c0.square(); - let mut ab = self.c0; - ab.mul_assign(&self.c1); - let s1 = ab.double(); - let mut s2 = self.c0; - s2.sub_assign(&self.c1); - s2.add_assign(&self.c2); - s2 = s2.square(); - let mut bc = self.c1; - bc.mul_assign(&self.c2); - let s3 = bc.double(); - let s4 = self.c2.square(); - - let mut c0 = s3; - c0.mul_by_nonresidue(); - c0.add_assign(&s0); - - let mut c1 = s4; - c1.mul_by_nonresidue(); - c1.add_assign(&s1); - - let mut c2 = s1; - c2.add_assign(&s2); - c2.add_assign(&s3); - c2.sub_assign(&s0); - c2.sub_assign(&s4); - - Fq6 { c0, c1, c2 } - } - - fn invert(&self) -> CtOption { - let mut c0 = self.c2; - c0.mul_by_nonresidue(); - c0.mul_assign(&self.c1); - c0 = c0.neg(); - { - let c0s = self.c0.square(); - c0.add_assign(&c0s); - } - let mut c1 = self.c2.square(); - c1.mul_by_nonresidue(); - { - let mut c01 = self.c0; - c01.mul_assign(&self.c1); - c1.sub_assign(&c01); - } - let mut c2 = self.c1.square(); - { - let mut c02 = self.c0; - c02.mul_assign(&self.c2); - c2.sub_assign(&c02); - } - - let mut tmp1 = self.c2; - tmp1.mul_assign(&c1); - let mut tmp2 = self.c1; - tmp2.mul_assign(&c2); - tmp1.add_assign(&tmp2); - tmp1.mul_by_nonresidue(); - tmp2 = self.c0; - tmp2.mul_assign(&c0); - tmp1.add_assign(&tmp2); - - tmp1.invert().map(|t| { - let mut tmp = Fq6 { - c0: t, - c1: t, - c2: t, - }; - tmp.c0.mul_assign(&c0); - tmp.c1.mul_assign(&c1); - tmp.c2.mul_assign(&c2); - - tmp - }) - } - - fn sqrt(&self) -> CtOption { - unimplemented!() - } -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq6_mul_nonresidue() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let nqr = Fq6 { - c0: Fq2::zero(), - c1: Fq2::one(), - c2: Fq2::zero(), - }; - - for _ in 0..1000 { - let mut a = Fq6::random(&mut rng); - let mut b = a; - a.mul_by_nonresidue(); - b.mul_assign(&nqr); - - assert_eq!(a, b); - } -} - -#[test] -fn test_fq6_mul_by_1() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c1 = Fq2::random(&mut rng); - let mut a = Fq6::random(&mut rng); - let mut b = a; - - a.mul_by_1(&c1); - b.mul_assign(&Fq6 { - c0: Fq2::zero(), - c1, - c2: Fq2::zero(), - }); - - assert_eq!(a, b); - } -} - -#[test] -fn test_fq6_mul_by_01() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c0 = Fq2::random(&mut rng); - let c1 = Fq2::random(&mut rng); - let mut a = Fq6::random(&mut rng); - let mut b = a; - - a.mul_by_01(&c0, &c1); - b.mul_assign(&Fq6 { - c0, - c1, - c2: Fq2::zero(), - }); - - assert_eq!(a, b); - } -} - -#[test] -fn fq6_field_tests() { - crate::tests::field::random_field_tests::(); -} diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs deleted file mode 100644 index 9bab427f4c..0000000000 --- a/pairing/src/bls12_381/fr.rs +++ /dev/null @@ -1,601 +0,0 @@ -use ff::{Field, PrimeField}; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -#[derive(PrimeField)] -#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] -#[PrimeFieldGenerator = "7"] -#[PrimeFieldReprEndianness = "little"] -pub struct Fr([u64; 4]); - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; -#[cfg(test)] -use std::ops::Neg; - -#[test] -fn test_fr_is_valid() { - let mut a = MODULUS_LIMBS; - assert!(!a.is_valid()); - a.sub_noborrow(&Fr([1, 0, 0, 0])); - assert!(a.is_valid()); - assert!(Fr::from(0).is_valid()); - assert!(Fr([ - 0xffffffff00000000, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48 - ]) - .is_valid()); - assert!(!Fr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - .is_valid()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fr::random(&mut rng); - assert!(a.is_valid()); - } -} - -#[test] -fn test_fr_add_assign() { - { - // Random number - let mut tmp = Fr([ - 0x437ce7616d580765, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca, - ]); - assert!(tmp.is_valid()); - // Test that adding zero has no effect. - tmp.add_assign(&Fr([0, 0, 0, 0])); - assert_eq!( - tmp, - Fr([ - 0x437ce7616d580765, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca - ]) - ); - // Add one and test for the result. - tmp.add_assign(&Fr([1, 0, 0, 0])); - assert_eq!( - tmp, - Fr([ - 0x437ce7616d580766, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca - ]) - ); - // Add another random number that exercises the reduction. - tmp.add_assign(&Fr([ - 0x946f435944f7dc79, - 0xb55e7ee6533a9b9b, - 0x1e43b84c2f6194ca, - 0x58717ab525463496, - ])); - assert_eq!( - tmp, - Fr([ - 0xd7ec2abbb24fe3de, - 0x35cdf7ae7d0d62f7, - 0xd899557c477cd0e9, - 0x3371b52bc43de018 - ]) - ); - // Add one to (r - 1) and test for the result. - tmp = Fr([ - 0xffffffff00000000, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ]); - tmp.add_assign(&Fr([1, 0, 0, 0])); - assert!(tmp.is_zero()); - // Add a random number to another one such that the result is r - 1 - tmp = Fr([ - 0xade5adacdccb6190, - 0xaa21ee0f27db3ccd, - 0x2550f4704ae39086, - 0x591d1902e7c5ba27, - ]); - tmp.add_assign(&Fr([ - 0x521a525223349e70, - 0xa99bb5f3d8231f31, - 0xde8e397bebe477e, - 0x1ad08e5041d7c321, - ])); - assert_eq!( - tmp, - Fr([ - 0xffffffff00000000, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48 - ]) - ); - // Add one to the result and test for it. - tmp.add_assign(&Fr([1, 0, 0, 0])); - assert!(tmp.is_zero()); - } - - // Test associativity - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fr::random(&mut rng); - let b = Fr::random(&mut rng); - let c = Fr::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - - let mut tmp2 = b; - tmp2.add_assign(&c); - tmp2.add_assign(&a); - - assert!(tmp1.is_valid()); - assert!(tmp2.is_valid()); - assert_eq!(tmp1, tmp2); - } -} - -#[test] -fn test_fr_sub_assign() { - { - // Test arbitrary subtraction that tests reduction. - let mut tmp = Fr([ - 0x6a68c64b6f735a2b, - 0xd5f4d143fe0a1972, - 0x37c17f3829267c62, - 0xa2f37391f30915c, - ]); - tmp.sub_assign(&Fr([ - 0xade5adacdccb6190, - 0xaa21ee0f27db3ccd, - 0x2550f4704ae39086, - 0x591d1902e7c5ba27, - ])); - assert_eq!( - tmp, - Fr([ - 0xbc83189d92a7f89c, - 0x7f908737d62d38a3, - 0x45aa62cfe7e4c3e1, - 0x24ffc5896108547d - ]) - ); - - // Test the opposite subtraction which doesn't test reduction. - tmp = Fr([ - 0xade5adacdccb6190, - 0xaa21ee0f27db3ccd, - 0x2550f4704ae39086, - 0x591d1902e7c5ba27, - ]); - tmp.sub_assign(&Fr([ - 0x6a68c64b6f735a2b, - 0xd5f4d143fe0a1972, - 0x37c17f3829267c62, - 0xa2f37391f30915c, - ])); - assert_eq!( - tmp, - Fr([ - 0x437ce7616d580765, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca - ]) - ); - - // Test for sensible results with zero - tmp = Fr::from(0); - tmp.sub_assign(&Fr::from(0)); - assert!(tmp.is_zero()); - - tmp = Fr([ - 0x437ce7616d580765, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca, - ]); - tmp.sub_assign(&Fr::from(0)); - assert_eq!( - tmp, - Fr([ - 0x437ce7616d580765, - 0xd42d1ccb29d1235b, - 0xed8f753821bd1423, - 0x4eede1c9c89528ca - ]) - ); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fr::random(&mut rng); - let b = Fr::random(&mut rng); - - let mut tmp1 = a; - tmp1.sub_assign(&b); - - let mut tmp2 = b; - tmp2.sub_assign(&a); - - tmp1.add_assign(&tmp2); - assert!(tmp1.is_zero()); - } -} - -#[test] -fn test_fr_mul_assign() { - let mut tmp = Fr([ - 0x6b7e9b8faeefc81a, - 0xe30a8463f348ba42, - 0xeff3cb67a8279c9c, - 0x3d303651bd7c774d, - ]); - tmp.mul_assign(&Fr([ - 0x13ae28e3bc35ebeb, - 0xa10f4488075cae2c, - 0x8160e95a853c3b5d, - 0x5ae3f03b561a841d, - ])); - assert!( - tmp == Fr([ - 0x23717213ce710f71, - 0xdbee1fe53a16e1af, - 0xf565d3e1c2a48000, - 0x4426507ee75df9d7 - ]) - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fr::random(&mut rng); - let b = Fr::random(&mut rng); - let c = Fr::random(&mut rng); - - let mut tmp1 = a; - tmp1.mul_assign(&b); - tmp1.mul_assign(&c); - - let mut tmp2 = b; - tmp2.mul_assign(&c); - tmp2.mul_assign(&a); - - assert_eq!(tmp1, tmp2); - } - - for _ in 0..1000000 { - // Ensure that r * (a + b + c) = r*a + r*b + r*c - - let r = Fr::random(&mut rng); - let mut a = Fr::random(&mut rng); - let mut b = Fr::random(&mut rng); - let mut c = Fr::random(&mut rng); - - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.add_assign(&c); - tmp1.mul_assign(&r); - - a.mul_assign(&r); - b.mul_assign(&r); - c.mul_assign(&r); - - a.add_assign(&b); - a.add_assign(&c); - - assert_eq!(tmp1, a); - } -} - -#[test] -fn test_fr_squaring() { - let a = Fr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x73eda753299d7d47, - ]); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fr::from_repr(FrRepr([ - 0xb8, 0x77, 0xe0, 0xbd, 0xe7, 0x98, 0xd6, 0xc0, 0xc2, 0x6e, 0xe7, 0x79, 0x05, 0x31, - 0x9a, 0xb7, 0x5f, 0x4e, 0xaf, 0xa9, 0xd0, 0xa8, 0x1d, 0xac, 0x97, 0x3e, 0xf2, 0x9b, - 0xc4, 0x29, 0xf6, 0x13, - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fr::random(&mut rng); - assert_eq!(a.square(), a * a); - } -} - -#[test] -fn test_fr_invert() { - assert!(bool::from(Fr::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fr::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fr::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fr_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fr::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fr_neg() { - { - let a = Fr::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fr::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fr_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0u64..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fr::random(&mut rng); - let target = a.pow_vartime(&[i]); - let mut c = Fr::one(); - for _ in 0..i { - c.mul_assign(&a); - } - assert_eq!(c, target); - } - - use byteorder::ByteOrder; - let mut char_limbs = [0; 4]; - byteorder::LittleEndian::read_u64_into(Fr::char().as_ref(), &mut char_limbs); - - for _ in 0..1000 { - // Exponentiating by the modulus should have no effect in a prime field. - let a = Fr::random(&mut rng); - - assert_eq!(a, a.pow_vartime(char_limbs)); - } -} - -#[test] -fn test_fr_sqrt() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fr::random(&mut rng); - let nega = a.neg(); - let b = a.square(); - - let b = b.sqrt().unwrap(); - - assert!(a == b || nega == b); - } - - for _ in 0..1000 { - // Ensure sqrt(a)^2 = a for random a - let a = Fr::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[test] -fn test_fr_from_to_repr() { - // r + 1 should not be in the field - assert!(Fr::from_repr(FrRepr([ - 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, - 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, - 0xed, 0x73, - ])) - .is_none()); - - // r should not be in the field - assert!(Fr::from_repr(Fr::char()).is_none()); - - // Multiply some arbitrary representations to see if the result is as expected. - let a = FrRepr([ - 0x6a, 0x0c, 0x3c, 0xad, 0xa3, 0xe3, 0xeb, 0x25, 0x7c, 0x81, 0x2e, 0x09, 0x9d, 0xe3, 0x90, - 0x69, 0x8e, 0x65, 0xf5, 0x42, 0x0d, 0x90, 0x1f, 0x94, 0xe0, 0x71, 0x8a, 0xb3, 0x03, 0xa1, - 0xf8, 0x44, - ]); - let mut a_fr = Fr::from_repr(a).unwrap(); - let b = FrRepr([ - 0x75, 0x24, 0x5e, 0x88, 0x54, 0x94, 0x4e, 0x26, 0x70, 0x83, 0x30, 0xb0, 0x6b, 0x74, 0xf7, - 0x46, 0xf9, 0x11, 0x74, 0x34, 0xf5, 0x3e, 0x68, 0x04, 0x92, 0x44, 0x8d, 0x20, 0x7f, 0x8d, - 0x83, 0x58, - ]); - let b_fr = Fr::from_repr(b).unwrap(); - let c = FrRepr([ - 0x0d, 0x74, 0xfc, 0x3c, 0xb9, 0x9a, 0xa0, 0x48, 0x71, 0xa6, 0xc7, 0xbf, 0x0f, 0x60, 0xa6, - 0x03, 0x67, 0xd7, 0x01, 0x75, 0x01, 0x67, 0x85, 0x83, 0x12, 0x55, 0x74, 0x77, 0xda, 0xd6, - 0x61, 0x71, - ]); - a_fr.mul_assign(&b_fr); - assert_eq!(a_fr.to_repr(), c); - - // Zero should be in the field. - assert!(Fr::from_repr(FrRepr([0; 32])).unwrap().is_zero()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fr elements into representations and back again, and compare. - let a = Fr::random(&mut rng); - let a_repr = a.to_repr(); - let b_repr = FrRepr::from(a); - assert_eq!(a_repr, b_repr); - let a_again = Fr::from_repr(a_repr).unwrap(); - - assert_eq!(a, a_again); - } -} - -#[test] -fn test_fr_display() { - assert_eq!( - format!( - "{}", - Fr::from_repr(FrRepr([ - 0xc7, 0xec, 0xb5, 0xa3, 0x46, 0xe7, 0xca, 0xc3, 0xee, 0x5a, 0x5b, 0x3f, 0xeb, 0xc8, - 0x5e, 0x18, 0x99, 0xdd, 0xb9, 0xe4, 0xff, 0x99, 0x44, 0x68, 0xaa, 0x8f, 0xb6, 0xaf, - 0xa7, 0xbb, 0xc9, 0x07, - ])) - .unwrap() - ), - "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() - ); - assert_eq!( - format!( - "{}", - Fr::from_repr(FrRepr([ - 0x06, 0x81, 0x19, 0xff, 0x98, 0x12, 0xc7, 0x44, 0x6a, 0x9b, 0xf7, 0x7d, 0x81, 0x10, - 0xad, 0xb0, 0x2b, 0x13, 0x74, 0x2b, 0x0a, 0xa8, 0x34, 0xd0, 0x19, 0x07, 0xf5, 0x36, - 0x13, 0x9a, 0xcf, 0x41, - ])) - .unwrap() - ), - "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() - ); -} - -#[test] -fn test_fr_is_odd() { - assert!(!Fr::from(0).is_odd()); - assert!(Fr::from(0).is_even()); - assert!(Fr::from(1).is_odd()); - assert!(!Fr::from(1).is_even()); - assert!(!Fr::from(324834872).is_odd()); - assert!(Fr::from(324834872).is_even()); - assert!(Fr::from(324834873).is_odd()); - assert!(!Fr::from(324834873).is_even()); -} - -#[test] -fn test_fr_num_bits() { - assert_eq!(Fr::NUM_BITS, 255); - assert_eq!(Fr::CAPACITY, 254); -} - -#[test] -fn test_fr_root_of_unity() { - assert_eq!(Fr::S, 32); - assert_eq!(Fr::multiplicative_generator(), Fr::from(7)); - assert_eq!( - Fr::multiplicative_generator().pow_vartime([ - 0xfffe5bfeffffffffu64, - 0x9a1d80553bda402, - 0x299d7d483339d808, - 0x73eda753 - ]), - Fr::root_of_unity() - ); - assert_eq!(Fr::root_of_unity().pow_vartime([1u64 << Fr::S]), Fr::one()); - assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none())); -} - -#[test] -fn fr_field_tests() { - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); - crate::tests::field::from_str_tests::(); -} - -#[test] -fn fr_repr_tests() { - crate::tests::repr::random_repr_tests::(); -} diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs deleted file mode 100644 index 253b38a8f7..0000000000 --- a/pairing/src/bls12_381/mod.rs +++ /dev/null @@ -1,524 +0,0 @@ -//! An implementation of the BLS12-381 pairing-friendly elliptic curve -//! construction. - -mod ec; -mod fq; -mod fq12; -mod fq2; -mod fq6; -mod fr; - -#[cfg(test)] -mod tests; - -pub use self::ec::{ - G1Affine, G1Compressed, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, G2Uncompressed, G1, - G2, -}; -pub use self::fq::{Fq, FqRepr}; -pub use self::fq12::Fq12; -pub use self::fq2::Fq2; -pub use self::fq6::Fq6; -pub use self::fr::{Fr, FrRepr}; - -use super::{Engine, MillerLoopResult, MultiMillerLoop}; - -use ff::{BitIterator, Field, PrimeField}; -use group::{prime::PrimeCurveAffine, Group}; -use rand_core::RngCore; -use std::fmt; -use std::iter::Sum; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; - -// The BLS parameter x for BLS12-381 is -0xd201000000010000 -const BLS_X: u64 = 0xd201000000010000; -const BLS_X_IS_NEGATIVE: bool = true; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Gt(Fq12); - -impl fmt::Display for Gt { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl ConditionallySelectable for Gt { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Gt(Fq12::conditional_select(&a.0, &b.0, choice)) - } -} - -impl Neg for Gt { - type Output = Gt; - - fn neg(self) -> Self::Output { - let mut ret = self.0; - ret.conjugate(); - Gt(ret) - } -} - -impl Sum for Gt { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), Add::add) - } -} - -impl<'r> Sum<&'r Gt> for Gt { - fn sum>(iter: I) -> Self { - iter.fold(Self::identity(), Add::add) - } -} - -impl Add for Gt { - type Output = Gt; - - fn add(self, rhs: Self) -> Self::Output { - Gt(self.0 * rhs.0) - } -} - -impl Add<&Gt> for Gt { - type Output = Gt; - - fn add(self, rhs: &Gt) -> Self::Output { - Gt(self.0 * rhs.0) - } -} - -impl AddAssign for Gt { - fn add_assign(&mut self, rhs: Self) { - self.0 *= rhs.0; - } -} - -impl AddAssign<&Gt> for Gt { - fn add_assign(&mut self, rhs: &Gt) { - self.0 *= rhs.0; - } -} - -impl Sub for Gt { - type Output = Gt; - - fn sub(self, rhs: Self) -> Self::Output { - self + (-rhs) - } -} - -impl Sub<&Gt> for Gt { - type Output = Gt; - - fn sub(self, rhs: &Gt) -> Self::Output { - self + (-*rhs) - } -} - -impl SubAssign for Gt { - fn sub_assign(&mut self, rhs: Self) { - *self = *self - rhs; - } -} - -impl SubAssign<&Gt> for Gt { - fn sub_assign(&mut self, rhs: &Gt) { - *self = *self - rhs; - } -} - -impl Mul<&Fr> for Gt { - type Output = Gt; - - fn mul(self, other: &Fr) -> Self::Output { - let mut acc = Self::identity(); - - // This is a simple double-and-add implementation of group element - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fr - // elements. - for bit in other - .to_repr() - .as_ref() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = Gt::conditional_select(&acc, &(acc + self), bit); - } - - acc - } -} - -impl Mul for Gt { - type Output = Gt; - - fn mul(self, other: Fr) -> Self::Output { - self * &other - } -} - -impl<'r> MulAssign<&'r Fr> for Gt { - fn mul_assign(&mut self, other: &'r Fr) { - *self = *self * other - } -} - -impl MulAssign for Gt { - fn mul_assign(&mut self, other: Fr) { - self.mul_assign(&other); - } -} - -impl Group for Gt { - type Scalar = Fr; - - fn random(_rng: &mut R) -> Self { - unimplemented!() - } - - fn identity() -> Self { - Gt(Fq12::one()) - } - - fn generator() -> Self { - unimplemented!() - } - - fn is_identity(&self) -> Choice { - Choice::from(if self.0 == Fq12::one() { 1 } else { 0 }) - } - - #[must_use] - fn double(&self) -> Self { - Gt(self.0.square()) - } -} - -#[derive(Clone, Debug)] -pub struct Bls12; - -impl Engine for Bls12 { - type Fr = Fr; - type G1 = G1; - type G1Affine = G1Affine; - type G2 = G2; - type G2Affine = G2Affine; - type Gt = Gt; - - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation() - } -} - -impl MultiMillerLoop for Bls12 { - type G2Prepared = G2Prepared; - type Result = Fq12; - - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { - let mut pairs = vec![]; - for &(p, q) in terms { - if !bool::from(p.is_identity()) && !q.is_identity() { - pairs.push((p, q.coeffs.iter())); - } - } - - // Twisting isomorphism from E to E' - fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { - let mut c0 = coeffs.0; - let mut c1 = coeffs.1; - - c0.c0.mul_assign(&p.y); - c0.c1.mul_assign(&p.y); - - c1.c0.mul_assign(&p.x); - c1.c1.mul_assign(&p.x); - - // Sparse multiplication in Fq12 - f.mul_by_014(&coeffs.2, &c1, &c0); - } - - let mut f = Fq12::one(); - - let mut found_one = false; - for i in BitIterator::::new(&[BLS_X >> 1]) { - if !found_one { - found_one = i; - continue; - } - - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - - if i { - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - } - - f = f.square(); - } - - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - - if BLS_X_IS_NEGATIVE { - f.conjugate(); - } - - f - } -} - -impl MillerLoopResult for Fq12 { - type Gt = Gt; - - fn final_exponentiation(&self) -> Gt { - let mut f1 = *self; - f1.conjugate(); - - self.invert() - .map(|mut f2| { - let mut r = f1; - r.mul_assign(&f2); - f2 = r; - r.frobenius_map(2); - r.mul_assign(&f2); - - fn exp_by_x(f: &mut Fq12, x: u64) { - *f = f.pow_vartime(&[x]); - if BLS_X_IS_NEGATIVE { - f.conjugate(); - } - } - - let mut x = BLS_X; - let y0 = r.square(); - let mut y1 = y0; - exp_by_x(&mut y1, x); - x >>= 1; - let mut y2 = y1; - exp_by_x(&mut y2, x); - x <<= 1; - let mut y3 = r; - y3.conjugate(); - y1.mul_assign(&y3); - y1.conjugate(); - y1.mul_assign(&y2); - y2 = y1; - exp_by_x(&mut y2, x); - y3 = y2; - exp_by_x(&mut y3, x); - y1.conjugate(); - y3.mul_assign(&y1); - y1.conjugate(); - y1.frobenius_map(3); - y2.frobenius_map(2); - y1.mul_assign(&y2); - y2 = y3; - exp_by_x(&mut y2, x); - y2.mul_assign(&y0); - y2.mul_assign(&r); - y1.mul_assign(&y2); - y2 = y3; - y2.frobenius_map(1); - y1.mul_assign(&y2); - - Gt(y1) - }) - // self must be nonzero. - .unwrap() - } -} - -impl G2Prepared { - pub fn is_identity(&self) -> bool { - self.infinity - } - - pub fn from_affine(q: G2Affine) -> Self { - if q.is_identity().into() { - return G2Prepared { - coeffs: vec![], - infinity: true, - }; - } - - fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { - // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - let mut tmp0 = r.x.square(); - - let mut tmp1 = r.y.square(); - - let mut tmp2 = tmp1.square(); - - let mut tmp3 = tmp1; - tmp3.add_assign(&r.x); - tmp3 = tmp3.square(); - tmp3.sub_assign(&tmp0); - tmp3.sub_assign(&tmp2); - tmp3 = tmp3.double(); - - let mut tmp4 = tmp0.double(); - tmp4.add_assign(&tmp0); - - let mut tmp6 = r.x; - tmp6.add_assign(&tmp4); - - let tmp5 = tmp4.square(); - - let zsquared = r.z.square(); - - r.x = tmp5; - r.x.sub_assign(&tmp3); - r.x.sub_assign(&tmp3); - - r.z.add_assign(&r.y); - r.z = r.z.square(); - r.z.sub_assign(&tmp1); - r.z.sub_assign(&zsquared); - - r.y = tmp3; - r.y.sub_assign(&r.x); - r.y.mul_assign(&tmp4); - - tmp2 = tmp2.double().double().double(); - - r.y.sub_assign(&tmp2); - - tmp3 = tmp4; - tmp3.mul_assign(&zsquared); - tmp3 = tmp3.double().neg(); - - tmp6 = tmp6.square(); - tmp6.sub_assign(&tmp0); - tmp6.sub_assign(&tmp5); - - tmp1 = tmp1.double().double(); - - tmp6.sub_assign(&tmp1); - - tmp0 = r.z; - tmp0.mul_assign(&zsquared); - tmp0 = tmp0.double(); - - (tmp0, tmp3, tmp6) - } - - fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { - // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - let zsquared = r.z.square(); - - let ysquared = q.y.square(); - - let mut t0 = zsquared; - t0.mul_assign(&q.x); - - let mut t1 = q.y; - t1.add_assign(&r.z); - t1 = t1.square(); - t1.sub_assign(&ysquared); - t1.sub_assign(&zsquared); - t1.mul_assign(&zsquared); - - let mut t2 = t0; - t2.sub_assign(&r.x); - - let t3 = t2.square(); - - let t4 = t3.double().double(); - - let mut t5 = t4; - t5.mul_assign(&t2); - - let mut t6 = t1; - t6.sub_assign(&r.y); - t6.sub_assign(&r.y); - - let mut t9 = t6; - t9.mul_assign(&q.x); - - let mut t7 = t4; - t7.mul_assign(&r.x); - - r.x = t6.square(); - r.x.sub_assign(&t5); - r.x.sub_assign(&t7); - r.x.sub_assign(&t7); - - r.z.add_assign(&t2); - r.z = r.z.square(); - r.z.sub_assign(&zsquared); - r.z.sub_assign(&t3); - - let mut t10 = q.y; - t10.add_assign(&r.z); - - let mut t8 = t7; - t8.sub_assign(&r.x); - t8.mul_assign(&t6); - - t0 = r.y; - t0.mul_assign(&t5); - t0 = t0.double(); - - r.y = t8; - r.y.sub_assign(&t0); - - t10 = t10.square(); - t10.sub_assign(&ysquared); - - let ztsquared = r.z.square(); - - t10.sub_assign(&ztsquared); - - t9 = t9.double(); - t9.sub_assign(&t10); - - t10 = r.z.double(); - - t6 = t6.neg(); - - t1 = t6.double(); - - (t10, t1, t9) - } - - let mut coeffs = vec![]; - let mut r: G2 = q.into(); - - let mut found_one = false; - for i in BitIterator::::new([BLS_X >> 1]) { - if !found_one { - found_one = i; - continue; - } - - coeffs.push(doubling_step(&mut r)); - - if i { - coeffs.push(addition_step(&mut r, &q)); - } - } - - coeffs.push(doubling_step(&mut r)); - - G2Prepared { - coeffs, - infinity: false, - } - } -} - -#[test] -fn bls12_engine_tests() { - crate::tests::engine::engine_tests::(); -} diff --git a/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat deleted file mode 100644 index ea8cd67652d133010e79df8488452450b6f5cb17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48000 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 Rm?nepfGSPSF*XppvfIk?3d#Tg diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 86abfba945c7b701750d637bffe6701330e10e88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96000 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%0A9l>2- zfDnX>M_o9bLjV&-?O#zHse{a2_A72l!z>Q&=aQsA2<8g&6cA#;a1-!$bcL;kGU73&locfS_AQGns@ zhIT6w1QGxGe7RQ74q{Ve>7vzd0rL&ORpxGi335=!%;(U3LNV5U>+w}8Q9IuG+}2y{ zp0wTqV|T(pc}?0kWh9XOJ97!W#rP<9f;V=kLCubnhkOoR3stSoy05P=O=MuJBGW6) zen#m+>`jO=>ZdiZi6aKCo;9^z<0z2bFWPkDBp(Iq5o|I*BOOOZy*%4&9Y)E?aaM$Z zI)%dU1Q>h)g*Oa2nK6EhXH( z#90Qtu%I;BwnE@sv*bb6e_-Ic^gp2#4m2MEiY!Y6^Yn@g++N|wf?dtT0h<6$z;g(+ z#Ba+s1DuZ%Jgt6{JhQ`H`P^^eXNY&NJiMWpXe^litd4^Jt=m;)F2%>L0JZ|&4w$fs zCG&aFvcwKB8DC)tK|q?ba!S+k(Q{Eg9f(YeC$IDZ%>z^So?*wdt=kBrMeLAcbt49E zdsCW*+yuDR5eT)>=aJQXg=%%+GYv98q{{|MV8cF-I zhu6O+j>VP=5t6Vf_?-lijWa}bPqQdXQ=u1Y_oRxtFo~U!;Cjv8OS<4X>O^wLC>5}C zYZ5`U7k@u%uftvWn(Rz@x@c3~oi7Gm{c)14!j|%Gbgol;*riM;MGqL&dF^#7+2F<8 zvW6}e4pQiNS=S#!Iz3NZqmv0xL&}8uGi3hrQ9gLz0mmGSU{ykm(O0j?mc|I@4;$$! z=#MP_XtmdG7xd-~bhia8(Ob34o85fxC$nTjRxynasWTswi0#2EAWkl|Z>;5DPE0$w zV;=RrLyz}@6(bodQvo+tw01xD&)z5Be=P}Yd@>Ds`XL|Wpmf$Xk`@$0o3?9X?G`Us zWkB~{hMNhB=jBZzo2(X1Gm^_nx!&t$5RBG`8`D6`$rleP)L<$sCulF7FQcu18Ri7G z0csS!!2=QiJ&~Qb)q|he{rk@(EvAoWE5cHVa3T78xORjj05l!*EuS?eNYwVsBG3~Y z+lTTdt#Isa5A=S7AV?hu4p)^FL#=>B5vR9}!t&{yz4Asq+Bw#~D%o5IFyRXsO>b>h zj_Y6~gy<9w#>W{3Fu88^{h@?|xUuEL4I&y!_nshy_@qCwc3U3l^AcsGIE4WJxllcF8Kk)9k@3A9!5Ago_jKl_NWOk50Es>DSRam4FPC zA%|PVk9(^}34{oN`}^VR6EZgLt)E7n-j)OKZblU-qHj6m#1=mnfwPDjNwhoTKRw<^ zY7=*S_q}F?swB(s=sWhDU`jxURw&<{uwo8;v=fC6%WoYm?U+&Hg4so={@@>vRTxZr z2p0eVbCgVLY zGc@NYS$XAPxcLR$w_f@Mx~g)0Wt$1oXtoCU5Gi?X5NlQcjYs{rN_``8hTrPl=FBG5 zJysx2|78UB2;>mHU^-}X!#v9i6p^W+XBF#LS5uuRM}NreP21oP7RoDGL~419YkUPk zTt5IgXkd>&6i1OOwl(qq?OO}N^R41@Ys|1(aL7D;^;QC+azAUAK;o-gZ8sZ)r%BQygWP@;plmUsX+DJ4T$j6@NWoo5cW-8{?&7F;-D)Z;CG z(3&_t0w($fke%O7X*dG?Ttg&4eTKs=@%_X@h*LI=w$1}3EC*JNNyn3RCS%^M?9GaP z_K~(yEF5yw$CFb|6gH9H_RthB&xfVZL0OWzI@$*Q@?p(e%++jiyP?0!Q7n5!0aAn8 z>U3DvqdKwe`{?|iH{lJ2-c$O@IE9>I4vkF3bKxq^^xEftJ z!WLzLr%U?(l<=D+b7K?8p%|iDU_on6Z=MUs%SuuUBC(h+O>3lmKS)msn9u<3*!2OB zQG|4t@=w_Znsg6%&yu`Zs6pkoh2D5M9Gx`b-H_(tL^RifJ0Cm9-NF=DtCO(h!7480 z*84FtJV*y>Mxo=1isbvPoVhb=uG05Fupg_pgd;lQ_)q+oD(}C=MUH_~k*j00FUx24 zx623p^6%580rkAB+UEeHK?A)}JW6aY<<3%5qlSl=K8Sq90~5kM$}&H))8l+|j$ea?jC-Y7*i%-^&2^KgQ8$xo55e;2|nrCyhk6BeGRMu38X|;5Zicig?l5 z1q_bCCDYo%FkyJq^+(ys3)PD`g`y8{?@)crf$$L50%F;#t7ru0zqXeBPmIA{#Jc^7 z{-Zbxh@Cfnt^$dPo^}dj7BFM{dZJIDDox7A<-UtVd4LfkG5}AW%z)BZ{#<@)54tof zlZgsmpv>B2E_xgU0e*#_bC0WU zyix7E^RO!tb~9}sf;k>ZS0n7cGkyZ0BdFLLQYgS!VZfbL0O^d`VL%CH6=#C|pWZg7 zMo)qgY21wxGPG(n=yvkWQUCyLPnQEk3pQKa(MN0vdQsPNMlcObq_SjCTnP!~pI$OMCB+d(%ua4OML0}bLhKRh&IPa{1U_B( z8%YrD@?pDb`mO}Y3=}CX{Zi6>4pwWwsVXY#u(Z6>JtS?8>qHf7e=pQ4sIR!=CYf?f zHebbq=>!xa$k@b;t*;h}R+VPY4r|W-yq&eKs$=}cc2m3)PK^hK{k#mx?GSO#6@}(N z>fi{mQ**;n;%;5Vy1G@%E*=3-gPM6bc245n_Hn&>#cV%|Oq??hYUgzT)AtdMw&)c| z&n5RTE35{3d*A24upegV%C#a5=WxncBdq_UWg zy^FDUkggg~q|^I;PF@$v23{xC>V|y*VNgabeQdSulZXMonky602E+sfZbStc*rDcE z@=4jM;S(i!WCXhej2DujFN)pwswu^SD3i#{K!qoHJ!%i#1b1S)o(}$vev(;TK97Fw z_x-w@Z%5yEupMjDDIeyD@Yhz zn#G%Qa=Zn_wRS!k2yE)_wU@Z!vKug}tlko>U}(@Oz(6{Ow_S#y(}{{(PHTytf^ds& zdHxZhA+g>`g|W##PDu%+jG9k%K>`HPtSAi9L&gE#^0v$ja{CHB<>)a<)2vmD zGoOn`sdu-J*2%2#@B=#M8i)vOV;y~tToROTV&T8Ifm_@#8&i-3$qglbgcu>JzVBYb zmnT{BzORIpWK%%3?w1L69D~^+&e@Swe3=uz-@Y_Zp2m$dr;#Ko!1-=4_9cZc@kzp3dfVXLG>;gW==GhaR^~e~XhjL#J4WEe0Bv9GPMA;A>1I^28J-z*)Aw zyZKA$pIc4@I5Q5T2P%elQ4?hP)`{Dn$^y zo{t`9I5q!ICvz(~RJOOxHyse9Q@VLP{rb;L;tZ%6F%DD_n6(0qyh;%076a8QTGQS!e+2Nx#Gd|?UzX6J;pcUy2BLz0Ch=llu?{Xw6$i3p}5FM zc;OJ-e=F3IV2?R>-`rD=9F9MUK~*JEuM!@smvR-lGc6nxaq@RTb8ej>v;#_+pB-o= z3)ko@nA^S&=TMkzhx5I;4Qif`pj%|YJsb;LQ<<*(*B99Qm+FAA`JF*G>u;ud{Zn=> z=O%}1XOQ%GP|p@w0r7K0hvcF&J5~ZL#@Y!0_xy7)5ZM?|3zWMAG{S@|@eut7uf34x z3NIYVM)FMr3%lkzo*6j#w%r-B_XA;^L~@}6pHnOOe>ZWcaDz_>)3{H`6sK5O{Rskc zTR`!q+P;5~8p*;x2to#0B!LW*4`QKQ-uA07)_+pRb;v`k?kpuRVWDiEO_vceEj z63&sm`PoIR>rWDXZixPNK^0zT|AQ#6aBE)`AtfMh<0|U0lrr&t$tCS%Qb?TgEAAgw zFqX^F+U5y>h9%d~%U3yIn~@=1-Tal)SpP*oA8V--%jlhQFGIX#mG`82Q`osghXgOxBqVVV|lrEoepZrM597hE^j&ZA2VCRP>9Zaq#u z$WX8whHTEj=m|P{o#%0w++VeGqzcr`Bplili)hy?`J4KoWnvH;y- zLew73+r%}f9E2F=j=zsv7REvcG9|EhXZBl_aGHJY0->E1HLVS&(^0oA%%tRPahTwy z`sEol`O`KwS-w5`rLT(|13gSqM~{myzlQfQfkN`4N>CH4_G6w&nmvQ#;x?Fqtc|`t2u^-> zZ5Cl6iFB9hzT}m1gJs#*x!F0I6oVZYZ}9}FkmhYqaO8Aj9yH$&m5tC^2y{riRR`Gd z@lVl0@Wkg{Jm zTrUHt0W!A$-3CHvv4R6R)Q+K%YpqCHyNhhS%Kf^97I?qoQDgcga>zwfIBss_G*Lk6 zv~Hc-|HYl{P}L6K5>(%T;f6oLu?xdZv4ykbx*ZWRz*A`bw(Pan>jcY@^iJZLXYc4Q4ThsYhHLhFtLTd?L%9k!%3@ zFL429xh&me1M-O`y=_wt@xz=^QZ-;yoBlFBrF$)b-OlO(aAG~0lg%SHM2F)i8z=zR zn)=7zat~$*f#MHIByVI?J{DD=tC>Xu4itq;9odNAFpkr7{9!OyWG2SX7NiYrRQhfT zvF|Nyw8b%yW>*oxXP6A*$OT)Rb?6n6ciq&n;bl1CJL8yTwinISCt(YLD@7`mpV`aG z2a-@lF)x(lUa_YVn~3Ug8!RKo3gL=8N*;kYmlDRu)E;z;jC~Ut{taSvn>XOB+8RCs zip(lBO$!x&gu1$pRdP)?j!TwWtYanl_+YUEBevR{PW=*;d0hr?UL@59^s}mV6Sy%s znIz8T92N|c>sPASWGQ_DzuJbBs{INizGIf_0xAc4?t?@JX(<#lwVcwSdZ~@XcNsaV4cRybTgw@Q4$teVO~V%$)>TA*4IcjF%zAH%E&7 zJ%Q$8--JnhscqZ#W0_?Gv@`uiuP6rG@M(h%YU{I$M=#dhcCUG|9{r^gN)t8sZ`qt5 zTjXLw7DU=U1VnzLZD^FF6+I27_Qrp<0f3UDpCsFEreU$_*MP!Y4d+4In&8jCBk*hE zV};&oM}?$(pOO<^qkRD3?Qx4VHL18w*L!+Cil=X2wK8IS90ftOWDKV-e@7{Y!v|Z) zLkxwqCJ7EDGR^^2TTWSqk+vDdcDm3{GB3vdSeT_aWz#STbYS2$>CovGkcpyp@n4B| zqn{`FuEP_a*;9Famw2Px>Z}=6()V`0I@$D(P{fD@{($8?$n&z@s{UH_{)VV;AN5YY zB~}6zwSCYfQ^|7$ENQU*&ia8o#`8i9n<^atu3OZ8I(jBfuEgzk!W|zk0nZZzBftxz z`xUTMVW0HAvE(y4yXO4;3Sz>grk%m_bas%O$Ie!nrvQd1X$-l$vMI@XF~bbtZX{V)I+xg;92VF7jt zCw-(T5w25pFg=Jc0gI??aL*a9l3EghO6Cd?fZFjA^@{Re<%$!t$eegS;fug9I7t-x z`-TYBOg8`p#E^yljp8l#;7BO2g2MiLSzFkIm~Ci-MfDe|l{>!CNAWd4U;UPDk4Hk` zA}w`;JDhJySl0y?v`|1~g^ECKA54+Oxgca0iW3e9!z-*FKEdK82m`Tr8-N7wvaNq1 zUN7kWx%uo~!9iE4IbWX+k_HVJ^tU@Rz{3)mle<@Kc@RJ;JW^W=fR2UBQiPUR-M)oa z5i=sd@d26DgamOaCzHTxTlt0b$mR#-G6TEqYTBusRQHBrC6e5#OxFVGs;V_xjn6P6 z0riSV$c6{`2bA%>f7N}iNiyE3H(Vtl36-;1@=M*MeW zvhoz&+dAcKQ~mlw)6x)uzP!+DNp{(i*@{V%m!wUi22ia^9uKiy#yz-nO<>mz{|s&{ zJG4IIT^T@VJsSpa>f;DN>nQ;V<$uft&fVp;E%%lVY@j)N^uPKrbX{qRi~gc~uHM;0 zlCd`g@8lRq@0))^XN$EqqI$Eg&gjfO?f!#K5)|sLunoX_!VlJ(^D&$RK4PMt|JDDq zeijL1?MZ#2GcNDu4MI0%&IJcjjF+Z9!;TQuV3%Rn&XSM}cJUO`o>Eagu8r{4>kmTi5*h!42L4^$A3|_%NE+cTp9OmpCp=g-eCnnnm*E z?b$~Gg(E!3N6Q1QwGra>g_cX(DZE_Tn&V&1L7?)lQqC2_k8{@MUuShH(7JYpU z7|zhjg`w8Dx>1(+ocXTP6sOn=kr)QmF$ILJ!MF+SB?&5R+i=fJ&C&d&P#ETkSev!c z_qLGwu5ofN-r5#kKtzKCm^hp>d*AWn(g_cwrgV`UC&@5j2LH@?eidsc+P9{B=i>p- z*FBe$G5GDxO*5BaZPJ1pWa%4Cl1BmH+9ose^W-B!VQU>2H`r@>%GMIqeulM+y%*^h zm*#^}uhMc2T>p;-S-F&cN9P#jQT0%OKE>vD7s6ik6p?Bw(*vlZ`BY(jJ`Cynj(#OJApN z?R#eT+L^!{>lGCYS&hag%~;KSG~nF@U9XbKyon=RI$b)f^^~ud`JS-wDsMG1I5=h| z?q^0U*`f(372NL$Ayv>ov+TmUcPb`ro^^SvMCG(H$}Vr9a#5zXY1_lKK)$mwegR;# z5&jo8F#z$E-&nyxsxovyp#`hsA@u9UuAW8@h;{_H;?|`Ev}0ZcKxEi=2N3CD-`s?z25-n=iU~D zi1n;rJD)GB%G?Z3P{(BUq^tNN7Ipr?5_i;|b63N)wt-e|YCSTwSK$L(S?e2$bgpKO zFuxawB;nUOWbG&o!`Kyr$+6;Scf~iN?I@r!fn~~SLCCdl=Hoba}j*XBjyW{}zxC#dx zsbgLpO+ds8I7?&zAr_pbAe-1^8Fh;I5QCB|QS}1K6>`j=v^pkI-BFNZF%SdFZp^D* zWuL^Oog@)pjuPU6l$ScLTjqG$V6em-rSKC9hE>k5aOViUwAj54ZJbZky2iN?bjwvo zj)T`crOJ#^!{dymS;pN=>Kgi0kth_7R=s9r3fmq}3~0AS^Ir(rf|>@~)y8`)z&+1Q z#rddQgk?{amKHl%)|2Y7Of(Q&RYSb41)!7weWQgm;8{%Hz2Kic%=v*|(Mpf5PBupp#@;z`;r zcCM+)!<7^w_yA6hd`Hhq#+*7;8#IxTZb4guOf3dN1l3$dU$%poG^6us37l)=w2eEb?9d{^=^yruJRIT31>VOMHFEFcwn$PFKThgJR zBwWs;qw3R{OlZDQRGEd&_yDMz%+-=Zc>@uY%*a=oMp-fp;X{dv(nUmoW`;X@@Z`!l zcnup)%}7~`Tn_tbEB2N$fZCoAfMpa3ROZwAkR9}8RheZfZ?oDT%NeVeOQyb0Ft6b; z^w41}OvdO7J4YsGT36(ndAbHvZUWwNLrWM5ZQl*{L%+@2RbqWhE&;j{>c z6@3x6sLVZ63@h!_)gx``70|wQSu`kin=E~od>puH(m+2n@qrt`grLGFZ2}Aig*KDs zK`+DxD^CUjK(1W^!EKIop)fid=X&9M%nUJdw~%F!fg*{?b1-aPeSij%+5j}f<67ua z{yM=xP^}xA*GhmOk4-+)ws7q9pA~V-tSGKWn&kE;k{v>IMmi7|6VOhx)VAeBA_Dbp z^f;b2I!qALgNsW+vn70x+gjqvZasWjj>cYPJ7B$qtvd!4mFm)-J?Uaul1yEyfYt>d zA$|JqaN-Qut}T7kSR!8R19*qta6NRO&7bv;NxI(?+s z38UYB_j@@rBoiP?I|bh2*R&q)G)3&139JmS2gVwf-TxXzlvE#_IEzt%k$tos=^VNT z#y{&@afgns{&M_Rn6Mezd&9+ifV2c~3f}FfI@=)$V~#rI0XkJ5#996T%c(FwBSbYjwrXV~M1uJZ6L0Be+xv~S~XWA3`r5q%lP`yw77=~<(+|+iRB2TvKv2JX%z!#RIhOvz6k8qVZPVbLBPXRUL`A* z$tDI0oI+SMexg_57)hkloip6=zmpP zzF-GWw8&&V-SIdMW!@LHYPd+O?mQ+)+{JJ3{-6Vx0vyXeE+o&?RSncwqXIl(Rm)rC z1k5ZCIpF#8^Ahb&lTl4jAp!)@D7XNjbX^#*Da&lCpv_HDtgWFKO1uvjP15C&D(J(* zZY3VtrOZZ(G^I23(sCo4FsD_C3(E%2WO;&?zkxmwWbs`rq{m$+ASO0SPSeExiQM1Z zo#@f))Mns>;uO_z{`Pt55acblQ&Ko{L|He3eS66qY z|Bk1Agf0e{7*7*qT-&9LM*24rA_9VW<97@w*KxdmKS893-u*j5(Au=iKdl!Cl;mL7 zQfmemY!0yG{)W_$B%A3O7MV_iI2lShd4j-_GM}wH z#;vsaxJv{Iwx;#S0uU#0TGjhT2mBRMAhqcskG&CSnM+*SZ?3)b-tQm;qNFMt~-C_>n6_z``N7^Lup z+t8(RUZE8!!OaZTQKXz`IMIgzlFZNXKaWB1YIiAc-b3$4|I%~oqLUiGFMC^4@QpZW z4d(#~)2f-u7(UCg@h=Exo&jHoSs77}w^F-L*VPK+J)A=W?2uDsoK9>rR_u-&BtH|z zDPi=Ve=|s~mvEMGdAWDw!YPe0e_Tl+k13)F3FA3DzUBA=f-yPcAgm&Tf8iEc6m0Z5 zfa?hOwsG-Bj65@h^}*2wg{D?kv6~nbm$kASUqwk^GnjdfH~O*mj(HXs7igd^vpYLg zIypUA#&i6+0Uebf2!pnU`|e*#Epg?j2%7fL&(?t(pb|_)YRnN3^`o&s8)(LJJ%h>m zX-``N@^4SL^7<=Wq))6m31w&G)*rx4CwYqLD7%SWEJqZ)j%%@(VS(Y?>}jv*zIf9U zD7c+sz4)QIkXCaVNKE`ibgfI0T1K1c^#IK?MF12UMWyBL@GXue14=`>ETl%3reg2l zQtTP7Rzd?7KAc9_j+)Xq9;9^jM{W7UurU%l6w+{uzaZMa;0-JW2cXmG`jcql(UFr; z6^G@Ah873J0~)E-!rdCwk1)+;abpQTY5Sc$h=DK~5AfP1GnU1dl*=_@)Nt1T6BgF% z;z&pf2H#g)haGL~93jzfe)JLW(*0@+{U&@Jy!xaFqB(eC*>RcKrsMI(N0ZK2O6D9u z%!Ea^SO6sybia}q&|n2%f=n>^`UXZ5Iw^Z40m#6harjtu4_D?>9N1h zwCRx2izPnWnR*#Lf1%+^9h(%a%$;S-+PsZO4`svW73n-+1^ruAa#5JJ_Vq}>sm2~R znCLZh;L#b=wgdP0kO_;CJ$5dsql=UCp1xQhk&ya1N>D@t^1g155lc{dr+;0%Bf!lP zVw(+#RjosUiwp&HY*xDM=1X^ix;F z{ejJ#AP>0I>ZbHvJ7)MGrASN(LHjF!+JZZY>MFd1qJwhro}~23B(dI;k|7v`yuKOaHdBs zO5B^UTP89#yrlvtJbZdIs~yX;U(Hh#f^t9_cn}eY`J)pGPWk+Og?6|OB8yM*qpO_u z9fDYZoKC5s=`Td924_Kv^z81W4?dOIyro~V{oDnQFtGp0xB8^#7b=ToMXsF4;tbZ_ zOVxBCYVj{UaWVgQhOiZwpv6+r%!SFId6xz^+Q)Hjs+Q2M zO)zBjgK6wIt&GA&Hn-y3{6-nHphxn{nba>}1Dpd(8d{pRii+Lv%WXWnV44_xP#1F# zFxlb?%f}&WEksN1VyO|)&yfMS_r?Odic3r+EN91wO^?IlQ6k6C;pQA8y`d6biFCE~ zhq)1UMNWO_$QBioFDMLa=cA0sMqjLDo!z}@{jz+M{;P#){N8w%@_%IFA(;E zL+ciJX=pm6`WWQhw0woYA1<42mqAD{E3GQ&@Kw^A4S=+!gOs-DoF_k}TYZ_RY9|AM z{f_j&|DXVrUEmr`Jsa5VA<_Cm*yohC=B3OF9eEZFRjgp&5wLC)w4f2X@w^II_sy9D z0se$&RptWAS7J&01Z6vL;GM&eOF4At+wuQq`j-qIa1S5&<%)JUZd(NQWc^o573Rh! z&+hezHepPFJsT88dqdk%(R*M(=Y$4wt$+miDqq?z<738jLdpsa1zcVw+3&N4HW*T( z-s77JiaX1)lCYm?Mk6R4Y87rg+VaP%sgTNb%-n(BVi^}sQN}@!NkdDlAg|Z2ckTWM zG`=`xFik_~+vuVwEydz=Bm8I^){E;6ru(<=1_boB zo@BFWKY&X+6vOxtQ}6^aYfn8v=7lD(B~QC@ zh@aUsB%Yqir{XH$%jU!#Ww8@t-fSqR@tX$6Cbg)HwL?1ZoePpJggmY<{a_$oQPGFxvDeVuTKaKGD^CSuT?;$=6b5BJ9EKZY53drf7|an!UlX26srywJ-!^4*1s1R z?LJ=pFGO7AC_lP9X71=eYKZEPfZRFb+mWXQwtR|KtUSG zYor(!t+9i6-!EfL0i#Z7o^`cuID$Hn_*||HLFs=pg_*w_w|ZKM^e76?_59M65zl4Q zP$KZ!V*b#r8_1AWOp4=JK~LjT#1_S*$E!?q5uWd&e=mrVw^su5+Q?6qNihbv%pGgf z(tQYmkA58coEAO80hT0h2tv6xKin$1ZSwdLwgq@W#(od=F^d$?(S7|qSpjC0vaInq<(q)Uhrfv=5dyK~Ubr^ofuw~y8VOpQa0r@uVw|>J( zmrSb>ITd!no4>7o>pO`R!>wipOP=|Zi47z}Mk>&_e#dRMf?q0%g zLw8~U#xWvl_M&0 z5DNP3lDt^&YZCyH4PTR8Ou03Ycy%BEbTa=Nw4I+~`xgdraAks7d*IWy*Jit+kUtIu z{hE#Ycz_L^AW*0RR;w|8-Mle!%vhu6;AY%A>-B9_9SX0X3<$_gmE_6N62?6|HN}y@pNSGJ@1|3HvDJx%HxG?_2GVL|b4*#)OW^x32 zH-r0#PU;OW^P-#uZ{_p6MdDVC0nkd@-QgJ`}D>9_1{QP!F;CXk2VM0=rc+s0ABt)Ddr3= zavUlX#%z*JUmTU!SAsN(SXJc(ut-tkkjk2KCQ*)c$hs8cbcl{*{si=%sir{+t;t@y zL9?PSmb#bMO~kUJHL1+OW3qS^O&f33of4*jS;7`-e;H9Kf{gK!*e2(Mz>DxG!va4D zMEr}nx}dRsGhhKVef1OXltY|R0f;|(`8x`bi&I81z6KeINBSrBqy8&QWY5{?m*bm9;}yW}y*E4gsQXv-48%~H`1ULGxy_h}ko4-_>R zE{J6vLiS4HZbAVsdwOtw#gPSz9j09`FrI{JB7Ti>5h?jD;!!r~4W3tc3=iKCOzizc z5C>BK7bv3I`PRKYN4W?N49F;q0b1eQe*}1ti^K|kjRxjkAgxMbmII&Rz z8+jS^uJ{CaV&iS4dy<26jZy=X&DkNpyK-S&7aKX@Kyy2}@HJS%0a0+s%#SYJX4DD& zUk4Z%tRH+XLHsIO1!rF=;Pe13{Rb)~U>Umta54a`)VrPlPdHFiXCLtA|Cc8Q9h4a* zxxjf}mKnoBEG$&0nid_>UEbTBTU*!!#PQED8{Y#y6iVkP$fMCq_(9cbwcHHIgNKUt z(-^3SWF327b|ZW)hF$0#2kupc6zC*-m|#u!qWt9kH=I0*^a02nO34v1ehoyL-e#2Y zjQ65-RW|&+g+Lsa&SO5OC}*$WXn|H~x|Pym=vASECSe+~GHS|{7L9^? zMs*t?i580A4Q7)gd`tf#J5vqu#`mkSIYh(GYy3s&SRt3PR~bfmP3(i?&~Zakk+j!& zL8;^9t=%Oz-mNZjz!?ijJ<<9(g&X!%JH(~q48_7R#yP%MYq@x2&=t*fhol5dcOlfa z1#AVg`tDzjDcleaJ?n+0d!LviQtj`?OdDnEl|((ujH+-hirNoHx7m8A7^)@6I!pz4 zMn)bSDZK)!ZCz^5Bd&bFTU}jYDO<4OC#Q!U)p*GE_#5nyeDR%bT09E|>$KLV_Dhx$>!<~GFz7nZP22Doxzh@VEVY6lzh^PpX-{bCZ= z(#;r!sy|(E!#V^lZwmDGSt01Iy4fx0F&h$! z3o9n%tGF4$T9einU+up>$L~0Zw3*B))Gt4myUCC@s&zG4Cw{qSQHtNxHyDLBh$Ui7Vmg=mV^^iN== zu-i|2z9>ok$o|VlD~UCY{Cv+3L>&;%RpdK=8mO@~yrDAytd%xzvXhhe#n*TiSR8?| zG~c0jZA&T)4sO?d7W{`*%efb-3I7^Ov+pkyad?4`g*}7-lDEl6&@y1G`@*@V03vkm zHIEHC));v+kNf1+qacrAX*{%0> z`6sx45Ll@;^7$Ejc;e$cLY?ii{(PNs_Y79(6e8y0KZLK?HPqOY1@~)Fs_Yh2vH~N zJiG~jva!OlV$bO&$|x~}DR+&Ar%2R!5neV7L;&B>ZIJ~+BD?=9}t(nmQ0T*Oe zo_`j&s4GuS$c*INhl()u!=Ny)I4}X~4yV+Ki;k~p*{)fms5%fnTF_z1FjiDb%G2pj z|6UFHr0UXUu^i>W_%bYp$oH3Lu@l1NyI+t;Z!cfD5rqP_yMxS=OiiAWd1hNzbY!o8L@3A8ggGvf&9u<9~!Cl7uFA^W%F<3}e>_|w* zWj8f+e%!YD)l4z9@IGM+T)HIK_F_lp zzPPUAj0{F^VwoIhc}lP3PC^;HTw*ZJOLqOC1|nxOwY?ibWVRcEGZXWiQk^*G#mZuO z*XXX?a*Hs_V;o+WI(re?BX>qK81Z|E-;BB1EF(>+TB&4FIIZaU zOth@T8pks$mb(do9fa`)kzP9taiUmn#CYC;{w6(&=j(dUNyip$wA-*-)9^%f%)c;r z0?UCNT)G%o;;7BW8+RMOeKwnwc@ly}v(e0?)4WC`C)R_=+X0pO1j~H}XEBs0(Emfe zMGpy>n+*CkT$*2R$iL9Gy~u>nLzX z8?GuYkUTXLG_Wo|xW+tpemEfw3CK)D9JzYO<~OAET_OuOJ@|}3rVjwJ*yu#TT2)ii zDp{?~G09t*UsfO4P#yj_K)P~Bitrez61Lm8yP?Hwuq=M~zt{-I**KE$3Se3v4u-bf&8t!1?`)>LzY4` z&(L<4)O?bcnt@FJ9cVTQzA)>!wmyT5#kCMOf^3f4Zo@?oIvWI&Ao4_Y zj-9luO$kw_4j|3-`)CKkZ4kLK#+hCwyU99LDSGu3VQvQSY)XJ~_P4h2EYQ)DgwJb~ z!^OIQ4)ROJ{agv{Fl))TmY>+fsifu&?5(yL*lkQbrVU}?u4BqZ2C_&s(`tVRkhd1C z&BWOK$|nX~n4MLaYj}b1>6Tx|KuTYzC71g&PKZDQ-fRuq^Irkp%D!M;?yV$3Jsef# zad8ro+Tnrz@D-b#N8S8tXSL3aIeRyG+L3Ewjcn^fJWMqarLtUESf3yz330O za*~D$k2`bbQRiIumv+n9Oy*I3Nt zPX6fn`;iy&9!v^?@wMX~no6xk%3+EfzQHB;WPo`q$7Ww%!mTD&Ld6;X?*E5K{Kax> zILeKc7Z^`F_)x&mKlr`zIa9JBD*cSG zlI@Kq+}pcH+|@#qA4@8n0QJ$N6y5z!y*-kF6++G?m)s0_dn5iC2|llsG7kCD+{xV}YluP8!s?Ca(!kbW2 zfH`u>HWNfc&j28Ya1jcA6%fD#97z!&|A_RWNPNH=b5agj=+4znaxx%lc4gy+zoXgf z+@C1}#e~N80&{;ZwUM3#bPplo6Rjg6m*Uk!++?W{J2+?>(~J^k616s&bTBq(6oB#R zq|Dk;`v8PM;0kjLDOm33jJ(aa+R0h439_#!uqd*u=t3EE01d`E*`|IKJU>e>ZAnxI zuR-BBe`j$O0SJ9w-GwxQo9%wFW%^LRdPSz>sr4ZSk>75Wtk9LjmZgH=cb3v9?;Fi`o0Fqk+#>V5jtSuzA`aq@h#pkdl)|S!< z?ki<4Hgf&iaWYXdeFD_ex7~a1h+*gOR{G{)D>o0>TsPYxqmY}?Gu9mED@CahbkQs8 znKcZAl~iN`XZIX~d=_v$$;R`(V*1QgY#x4gfZ>Y<;o!_13zYqU*2QiM7-`_~XZBwj0Z;`gK>^dpWb z==uGsLxh`Seaj}fu{q&qH*2K7d6uHuj0O4UDLsgU9j9mr?M0*kiHXi_xrwbi6bvN( zrPNsKJT#O^d!%OZCqutPug$TUi_CFAa@s7oao7n6E!i$bi1oHj=uVP?rO+E=FPnYf zjnKH~)0e`9Yx#zlQ&1z+^uUtC3{Ruj&a4Xxj=BzyBug$08Pm$h(?vE|9ia!|(X7q0 zmGLRTli{P!$EEYzK1rUP87V7;-|X`aKa9^8iM2aGZgrBS6T|QyUrJD?0uKy{6tBP~ z*|u7W&@Rr2&GetqL=0jk!$tWBbZRUTG`Jm(<8$rYL#^TS;D%uZeCj8{1<{wa@P)mC zz@OOSP9{JS|K^Fmd_{3=xw^VG0xKL3lW{?o`5vYPPtnje9+({j6KJobO`eL>8iUif z5GYO3ikfzNJ^$h%3xxNM;Be*_8RT%W z97#b?i^}e!M2H+$|B%{hcwug>No*5e&s;o8dQ@t7Vhnh%1;mAPQ1Olp<1sMWH@N3L zXKzbWxp|QZ7n=Q5q%oFkouiFZz$Dm{@@r4%w+|W*3>JWQhMb55(>Jqd(?192&M)3NN)Uppr9pB^d}q)>s=EA(H(4^a#*2m9`ZH5n{a7=O-8EY^;ST z0)$)Vqb%=Z!uP1~+#jL=EjZ-4)@p$l!8~yBk~lT_Vb4mIm<@f`jte(-aaZTc*lajY zfQ%CLX48p~@Sr1#?XCXG03qB1b2+Li&x{t`yLezCQq482H#K+fh}K|$b-D2OiQzxh zZCy@?O+7wWV=X7M=L?z+@*{Uv36otGh+FqX5vMTBJAa#4?6-Gf);lNsw2cVLgTs=7 z{4ZoO_0L*l^c#W>N4_WeGEeS${kV%$CTS3S>!Lh1>UBg7>pGAjaI0s}j!l>!{G<`# z^&jKS0Ve4KMrpTw!}^NR68+Ovc>C*f8HV=U0`f=(>Ae@2s;JpEgDeNyVD!kr> z@wMX)CZn00Y?EhzZyfk?%#iVLUzwi4aHpdN=f(zuak)AS8ZMZZyG32bWT-zk_XrOY zsL&^&az)p|Hc~CD*ibu;@A(Sq=m#?&4{62>a91JkxCHC(#`NDgl1wxtN%RjF>DWN% zEsEr~VBBMjovZxg`}m9RF5l~WzRY*gQ4--HMSZl`{qFxq<`msvq4*nWsxKW$WoF98rPkRt;0r9LUvCF2o-VK-EDGk;+cVv zpM?&mEa`MjnhKTp*Hb{5z%ZtcB`3_MIXf66u`|;S_$|~8PnfPM_3pA%`MiF8K>(?m zY&l5Fs~olV#=_rm^7G9ge!NS~*iy{Rz$)Zgm1`jf@_4{6EM*S2>6nk5i4S&vs6$Y* z9Up#paX1eEG3@lsOb&}ULK_hS~?KYtUveyV_G2PAxpzVJZYm`A#&;3wlsV-LgW;ZJLngR>%-6wJuU6#|rY~-TS`)e}^b)?^z z?|gNwcc+6Zs$exr!okz+-h24Ut~p2{q6mW#wwMGQePz!RUoN*nhV6g@M>>EV{G-Qg zfd@7NqZ;|r^GV0M+;q(hj(<(t+4m!StUKLvGj|NQba`KAEQF{4qcu%%uH@E~zOwJ? zK=EC2V|E`}p0O>LP=9kUDLCad z0)eTCDmHtU8!8m?7_am0c0(1_G>%VtZI#E-&-|HJVI z@`r6e@-7rX;~-6bkWS5>O0Tw~`S|z-AFkouWlG|{-G0ihGltP>AG*k6fdQp!c=9}U zotq(q=2vjJ(4__`AMo$ftreva&|(OqDCxlT0B<$cn3{prq__3)8`#?fl6X%8TADbf za;E=fOn_4_rL>zi^HyOH2b}S01Mi9Md=v!YWV##;cLlf9&w&kn3E~aPlSY3lM7{=^ zuO1H()1*EYHlzlWHOXbfs49;GH||FyBn^ebBKC^HTmilcl*yWyx9awW4O?@6>6e>F z+{N(|29t5G^f)r$1bcTGPX5wxvH<^sYZOVrOtDt~0yxJ60 z(40&{H}^aBuRCl~{fs7cpgy~m-=!ImNU=Uf2%!=`TgvChr05A4Rg~km+8aqzAE(qS z{xR-j_^m!0cT5r`b)pE`8SrA=RX4CcbO}nX)mAwKJQ9KvX;cT*eHNZ^$$gwwH7~{2 z*GGp#J+97iuJ6}=UGu0b^T;cw((!!&&bToJn62;v|K_9|L`Jkj{$?mr;&bv@2$4M< zX*93wI?(61zFgY~(0g;~r!8q~Bv)#3&Sg*)K?H3tI|r4n`&ztQWQWE+0t7+aGc8x% z#A@KXZpimtepog3S;sAk+AD)_X)A~p+{;#T6Gn$xT98(Jz5R{Md8HqChF8OX=oz{J z??of8MKQDN{o2IIczZ6M8Xm$I1a7;qDhqFOd5d}vve3Fo#hsc$-D#*MBf-fSmLQ!}D&BgZ_%D zOkDa=h}=aNeEApmrO}n@JtqZVLR{IPsUK1Wg#j#$S=Z}|?lr&RX}caGq0wKitL@6_NLiVR%*!$SVRp(K#Ad zAt_0ytip)rW9>O(J79%K>{Hh%WQb%ZTgxT-Ye2FJ7;Qt#;{@gXgs;|=>%p$bI3VBS z_-}9)rSbpAx>xK9DUc82n|mG0>$4mF(e1Vww?~Zi?V&{rY`QYC5Fnp)b0f9-FOKD@ z_m&}3+3S|7Ob0j5`FbjwKjOr;O4Ehp}GV_$MtFl~#}Drzy;SdDv5D_8|R+{)-2oF;`?H6(+!h zTrZYUBn9v)VOEC*esJHQo*CiT0xPfUtf`7*3qLT=OgD%RdogUSuexUnznqXXW|cDM zXMsQ{I(c;xFq=|o1^hPCVPRjaUr08}r-UVhUTV4)iT{SRo_DxTdeB8j4q87Zh?yeE zmA6&{GzhKX@sHhvHFy!;3HVqp^ULF{wC-*(U9lOcW$_W%f_JkOaMx#o}v6@EsTIn>gh|Dmbg zOFD%FHI$hvyU^A`gHl^pBJi_~pT2Pc6o;MIrjEAfe@UbYi0y^Z{Vh^X8Lx2ZC%Eu- zThX1z62cWHLP<_~&@Yh0I@6*O!mGY1act@G5ZH}=4A=x`aL=gK2b*G}APHWJ zj5fk>{PZezNkttt_GU~)`CdjAGY$9Mql5vU>Dqcl(%DVi^1E0D8#FvxZdaC?E`*NQQ-`~L{nWBdq*4(J=w={PEa z@Ujw@qcPQV=lZtsnxGr}1 z%x`Firl;x^6S5{4@v*(sKt77I~C2gN`X1Z`8>C6 zj${s z*8gb62_QJ$R1cP7-0xx}s4++sA{VfVO(VV+IIh^7YDJ;quTe`0#JM7gKX-};W)5IT zV^tXa+b{MDUxb-9_E<%?aDL-s#4{sB`{E|JxdGp^Fj57T(WeCwkxT6qVcX1JB-4PA zd<{+UN#9YMwL14bJ`1va<8<;^sQITB2~}J)>Z|XGcnc4VV9o#uVf^FlS;?*D+6k2U zW`RF*+`bY50fRF<_=IPjUP0=e`m8k>hp&=n@q=fxqd&9*+7&m}zAXc+7Umho)17n$ z#Wj{`3eKGNQVc00Tf3r;c!G%8I_lduvw7R^_uq zo^${LuIov81PjRbh9th#Hw|R&!Tf2W0t$ zdQ0>wyZd#ALL!djydAAcGBRcmET-nH|Am3utcoJgEuv?Jzdgcde+i}&Of5M!Lh5oElqwDs;?_RR8;aDcd`JYle> z{;QD=0aFR(eZ*%EB-WVO;x51M)r}yhLI+g&=_IXJvNQu0S2=Mp0fKdR%8s4)f}X&3 zAjQTXy(oYXZ!S|2b&_m7CuLPDX5@IQ#LEwXbrGhdky)XV$LqHLw=EJIfP$=f&p2nr z{vli!VzWwV6Jcv3y3Jl->pB~2;FgjDq?{=c1Jsp*mZk{TuADTuQBACxoSB0X}9aF2KTWn$`E+vTiSlTM6_-K!oB0kyDWM zY>T;}C#{#9NQ1dj{NV;`oZN@nVM{pivrSX-C!mDPBfV=!7W;5Wh=nW=kk)h*^2eCE z4B?c-+`6A{CVD7X=w&vf*B`I|$i}ve!PV9p7^O(%8duu@jfHdu^T|Z8AJ3(F1<4s1 zR>)MFuZ}T%Luy|v1ZB}ql@QVfRrlY8uL7j)KbDIP4dbb zB7=!I4c6-v^dj$oE*@$gb^hQM?~alJ9tf0e?23~+GYLW4QoYG>#&}}yAm-UH4c=gG zAkgObsIBqnez%~fUmlIkg%hj{EU-XEehD_Z#mQ{c0pOe(dY#+=GhRZM!4uWoX;2wF z++O(MqH#GJp4HWVHlubE0?s&8ax&1U;TWsFub*5v^7(@+Uh&M9&L~tkafXgW?7K-< zvd>~C6edCf=?;noGOdgC0aD>4xWx+!&@}jUn+Ku3vFL0sljf}?nFJ&6yF~$R)H@?S zD&~y~^_XQ32C|SM>j)g_9%xmkt;dFh!D?>%({G)=3SXWF66)HjY*jIb09kSr02j~2 zmn%^dH>a-;tNoV@tF}RHZ-ZnNDnukLX^8QU3^Cmn(0_FmDL`v%WEJTd_Dkg+=aI)5 zyJzFx&HJKva}V14pD`9-G8v3dcnM{xowtvoKw08C0e^A5d%P-9e9yYDpUQ<4E1XURP8OFGVxTaC{8@Je z?hej3MgVNbZ4}2l)Wm*Kr`)PU+M8;$Jf|SS&oSK%n3)Rz=G<}9*Rtgn6KVrEv6naP`?}xW64qtz*JJqgU%{u$RUrpkbFTqA9>pFR%jL}kK12YJovXK z8CLohK>~R9T&{!gUW3j(`{ZrI=Xk7MJe57Iw+Zx($9JCV$@uV^Mk4O+f1=0CsMI$~d4e;Wr5pa0@^Nk!jxT~R z&B2Pan?W=3NWgF3-Rry-!VK&1R#a`XgXqwf4{ur*uEuu;wzFPDe(ZJwVztOEiJ>EX zm@e!=%|JR>7ostiBsxrI4vS)RJ?dvqSr;6%!MeA@_u8!+C67@UV{`{&m*cJQZ79Bq ziH{-B5_?#9!mmE6@|T3p-jL-zmb$xpOT#}Npk9C?b7fWnU`b_2px*OsT5+t?>>Z7p zI zl2XEV55m8^^(OS7enb=*Er?vva>zN17qjD&KFLKY4|^;JArA6bF2fM ziEi!{G7^!$k36hjsIogCGIyH-Pg_c$7U~-U1|6JxK6KCqkXairkVr4|L@)-><3(8u zb$D{|Td-&M+2DGfkT47e-Aq!aX9k>1LFyY@I8ZKo;X&bZS%`}O&%LQmCb#$gkYbyv zQk%-ZH1@l?vu|t>B$W(@>Qnpf5faJOQ`)~*QS9Fx{*@)#0Hkr$N?V;M6D*OgCcs}1 zN8lLXD2J^X+6$5mjx)o0GW3m!6;&(PJKWH*T+6$n1c+U(t}>G{bZjL9G`a-_`HLpzzkso9#nsU*>vu2?ZPx-^n zbg5`DYzsS7?{u=|IAroo0ZhzOV?%?-oamk;x=Li0ku@OYK^r9X5)!Bn9$A&WoHx|? zKG(^*oSFswr)WIU8rB$#J>q8%yh!Yoq4FFF8y@Q4L5nbw|s{yhjd4uBg7bO|fZ^oRkAdp6Y0g9!?7=Jvor3NU9$U&5d-+8_-@rjfRymW8 z`q$BB=2}*IexXuw!YGx}_eHs-iOoa`a*+D-c^U@LhtsR5Udoh)G>H~`C81}SWZYyh z%w(YFDYlv)!5I82Bbv4_eFD)3Ns{UfODMKpauEA5hl%~RN=?5vn-n^Ez?);BKTCmMzR*23F@0|w@OCqd@WpyPzlteyHlF_*R_es!N)`h=w}WDl%-BMdbx z*a{R3hR@tcN|8li2!BD_J0m9oxDs)6M^*8wd34o|7JOrjZ)VN)Z89b}SS}}Af{$4f zAPVk2oLgXbQJ1wcuOBbl?h^-&ExP}AxMSq79@ zq~2|8RmI~yMgK5XA$R9}Qej733Waf?$ zgYsK9sD!TeGE0Usm^-TEE?NsC8Udo;51{T<6<&efku?nrx|r$MiCHHqhYMvQr3Ccl zsLI`we3Ot)om$+4JqHD)yPM!lt*lECoKom7w>u#N_9zsUk~P1XjwZ0YpE?OP@U&$V z2m}~y58zLt(EjgnWfcQx9J!lbP9RJV#xw2+&j}(5p`|kr4@08Z#87I)x(kp47!RN& zD?Zu3q^;Nr3iraMkgbcci#v(bW0iFZjxpK}y%2Jy{O_B;XmW_sm~Yf1ilxa2ms2Wr z(p!1pIKCT)A*OEB<&1*I{8!&c zg{yAkA1<9ed$~TOwq&LfUoMt&Pw&{}&%T~pa%tD0AZ;v{;jNGhKK=d)=bQmnknXe3 zHE`WT{!`}3aNchVyq*h({|f)?&vTl1bUen%9iPeMFJ(!S^KuAOaX7dCu(&zE4c)!W z$HW-t5e;k$MfqF-A}PTVzjThQckw{yw+$BF{?G(JopU^zZs1t4A#|xz99dW_ACd7N z)-UH4S1?C`no@^6&ur2l6vmr|ifK(5Ne4V3!X*i-RZg5~MD9CkaJC65)~e!B(w%q} zp{9|J`1^A|MlqhywlkoPUI{+Nw0+l!LSHdh4AGNPGajP8P~@&(0h^{w2^!xJ12~C- zp2>U1CNIk<)8e+o5~bz{@u(*alwqiQZwF!hmRWG2<{5kqT}ROeeA)XEnZhSVF@LqJ z96NR|SN{D`%$LIdHJxE;_PH)$r?PFz>1C+MlboU+!+!DEf&?WNEH_q`JJ-y6y>E9> z8AUE)V`tzf+9rtlNzLqKV0;|1Kq6}7d~P86!|8n`3IG2SVWmtGuk!R{bk!>0LW~HH z^bR1mZ*cip`|h7`^%p-v?D}MJVAK=120tkt6{}$oUdx>}l^7%?#=xuwvz&SR$ zHXzM+Zt<}|1Zrx_*Ran4D+lG}Sab|klZYV?S(07J{m9+-qtujSTW!sCcR2Gy%S>bS zh66Z{M5qnr+2QYp&UVd|72GBwAlj7`OWZObV=Y@-kAaQV@k+k4QaB9aIUD7u`fGX~ z%k%GR5{^#BF?jbnJ-(&~$7O;7b&W%y9IPp@2td7GY*YWpouOEfzi^$>6EV~#xqP8> z!X9C`QL1ao$V6CVoG>C4?n)~YNk~+?T^ykPohsYbh`&+LdJ>zF7AS$Qrb2XFDXL_! z`;xDABs^|YPEAe+wOhwyu`vwYCy>D zt(_&7BnKS`<7AxvRaONN@^o!M|E$lBSi0-Helr0{-dHwgqazuYd(v`k`UVhyPFr`8 zysigxy(fzl0KvtbE&0#eG5(>MFs- z9=TZUF$8*BL0*Q3Ag~**^18M&`3V<;=lAf&QVYru2_kj0J{f3Q;}vxR6ZJw2#~oFg zBQ67|@Ak+clD)Mb$yNycDE<(=d5WBJsPhJn!!_gWrr+l147O4U6K9yuYaqfO0)Q@e zhC3%C;a8zxc$0WjvIC}+=f@ClOfLeFalH15AXr=!%uhrJpcs=zx6SiGoddjWR#rfD zpSPxbM)oKx5i|gm))6|H9LhOyI$O$))HN3a!S&(^UMCtaQLE648A%E~6YpHIspW9% zce<$)+j+m&xw)K1M588-&Cx#Tk6`2(U0=2bF5I*b1~^k99s7ZO37jY;r^a}NTW-2N z6VxHi#53F1IasCanpAJRD&aXcr9>bL>#5+MBo-^DAZ`IM6x*{fMm2_sQ4V@U_Qzn3 z!KRrVM{n%}=jF)KVDqOHc%1kMN>+6yD<&xHi!$9Pqs?0A446%n z!>UYSDdt6nO1ub~a6%9ig&0mL4;czbKZfAYJX82EmzVRpehf@~9RpH8NJ_iL+U$b| zB6r^A8#DIC_GnxTAzCB>?IluUblPLP&@2rbLHq&xUsgR#7vJY9vI%F6^_NU#VSufL zU&Hh=$Cq{?RK6npA=wBDnr6ZfY92!I>l7W@xkLCRB;*T6#j}5y&qKI;w$X6xFhOigN5A8w>5mYhDP5VTTbccg3uKT@yNOVl3cx51Gj-Q-&|!5)s1_q$zSY z8$XWwSkr_SGZqSJ@=LCBj1{dhc9|``+p}5~2Cj|`>eaOz?N&B(ic19)3$J8&1sWqh z1y13!$zUkbltpHifYyB!x1IgWoiuy{C+q6=2T)r3n1y38 z`*LO%Mbzp%CWgyl{CKKH>FK&m;fZIH3OJArPgf}FVeQAJuL&mPLZSUMz9spLBoF1y zYSdDF%rNLOt|Bj$3=SJto`k zq8Y;83n@L5{kO%hm4w5f2M9?EoF5s@8QjeflM*=mfeL@#P6ak}(yR{gEU-`~oL+^= zQf4y*-dsp=d&0h*Z8=c`SGP1%-Oa%kr8S$znQmN;^Q=fik{Tq_CIhXRPLC2)L5sm& zyej9j)lk+Qc{UId1%cQ;0j8)VOvnm`{lCYik>YW6PhR#&5?#Q4;1YMZSRov-^nTJ^ z)fEZQ=?~ouHKry@#(!MN(DuOz7!G&AW$pJX({Ok#fo{_kg#Vu6+SwyFKJkJc!PNN% zyB*j8sd!E`KlU*i4pzv}TWArPt+sBh+teA&zH@u;|9JBX2Cxa=AuWjgyJ%Gah?(sP zNo26vl>#pfHzyyHp?CuIHlM7tr+x*mSi(pFPqb=7c}c<^;86D;(i5ZY@L4qs*#t7q zxIB4@6==Fk)_9I7KLRNH!;$eS!#=5~Py%6a0CUS`(+9b&k_jwPBr2r|@DhMjFs*Cr z6QVLjNG}iqPz;822ynLw?Hb&|KeKEe0iPf#Asx9YP#q;Y2%8KRMhNh%UNw@XwH)&j zz3)B5N~N~32yHfOz@tps@qd<{Rw?Yg%?#STbWLYT5@{k0c-gRmy|(l5BNia^Gtk<6 zR_V8$12Dy+NRhH25cj`A#!X7{i0|JJ?2)ItPqe}qSn81if2Qqa4h{jjf+ZdSez%by z&EW`f6mAYH?W+u_17)O+8U)bcx7By3^P89z2ufO%bq-HZn80>+#4a|L5oK1VoAlyk zE3MjF%Ym5dISh}H8354saToS>Fl(|891?qFYLf?b2OPHr`LUH&cFC$8*U8Jn#NG6U zg7MKAIzEa#&ao6Fb+AhC8h@J^Sy^cb?c@mfPiSu2H~>jM@smr?s^~+vJCdcZtH0a) z_s^mWIP>K6E}Y@djs~y+LDuM`h+LC+l-(km*r;~zSqvVNB%`Gt(Xw{S3el?sRds(< z3};5B1EP?vna>x9Q5HQ&b~t!^ob?unnrg^I zYMD3kXa9c&!BVN(uZowl#znZC^vSn&+j1cXdM>ke2DG? zuH886Qpog%{Qa=Xm`pSJKe&8lrF5mHW2rc#rK;Go%d@wu+Qf~E3bzz=u#b5IbDF?; z%sns$r?n}U7Yeb*j=CrCnfHyjoKN1YOB+tJJ(JuyYdLq23{QLqd7w-Xj*q4tjDF=$ zT_k+^7m-BS>2I((S&*(>IASc|(*={ojq#aj`Wn~J!B2|pVF-T*`6(SJsedIi#FLeX z9N>nF+tqT&Qx*aSyW<)wv7C(PLH92o3q=+qkK>W$2C4)Y6J0%x(u1-Ge0Mf?93M=+ z=Zc5NliKkJF%iIDc&z0pS4bMyRkxw+Y<7fe{4dlDc+{Y#JQmz@qu>vtWnGe6qbW906-N%&cIzz` z1Lgk6o4qksbUNa1Tg~ivGZML{Xpo-(MI-Z&C_0s%7rw;e1^2TwO-T(V(lg|x7oeDf zHm}+xHKl>aMjj{Zg3jWYr4u*-BTBu$^YiUJRI3J-W_7ga+G4PQ(vwf>u|8O=z8mu(-4z$vPNmagTe**R&6FK(1#x?>+~YR4{f9pM~6%I9@MB_ndlw>h44Weofl6<86@ zP$C*iOzFci-Hj!)5Wl)EKfj*R)cU3`s4&|2gKCPykpN7#-<{sACa5Dy>_G zhnN5;1D64EcFUm^`mos$@Be*)1;99LporD+3agrbIt7Ckxr^S=si{%i^?x{M;@GZU z(}RKiorwv03xIBbQd!ICnz??`1U7=Fe{gk3h@4Cl&s#5uOovzuf%2-je~RD_h?S1R zT_T)?x=84jdGl{+{9>O4tW0RUEG+lu9JIw0%IzIZ=*_E`)@gVa`utiUR)L8bY3YZE zy<22XN6pg!P=_KY_t4m+34FsE?((G&lA(pQ3{MbZI|d0gMpX=sgk`W%@c#x6{0fes z!qt6I3#7k$H4iUni49+SG?Zl*5Gd%Q9j31}>odvH4c~(pEegjGd4k~N?r9tTh>qT7 z6db!Z70HmFWbrCH*#z$d7s%mEaFO#I>-Ifaa=r(Spi`s2ihOt@|L6 z+Fp5(XIaG==%j}i%?*XLQ$P;Iysuyv4MMhO255F$2du|W?WP<~(z0Ly>d=LT&;R(O zI1&wUgFONe!&9kM48yyWCcWh5b;t={M;Un~K1C!?Fl2gpmQvWD)OkMP)^ zg)+hu^&LPc@8Hhb0v$V3B$wnU|JDew=cnG5EKIr+EyT0ANCZv1zK;^5?6TMU{Ljh; zHm;I|w%f!68(7c^Ul=}0cl|}ydOHS*U#mw&qYq^C4 zoaMrUS>GuN&rQ%~1=sPYCa}%PoR%trr3fcw@;PBFw2a7^>bW1qRyBdPTS zzg-7Ht|~&7>|sa^UsJHf`IKGR1%o4~P ziOE#JH}fu=O~f2D3{{PrAS+&4^^~M*Nu{O(LHfL$5kB%}!|_pH-!vRoJ+tHy&F_vT z?^0S{PuzwW`XNo6NLq#4Nx~${=*5}~J9jS$_{@2nDdVgmy}4VlH=`*K$h8#MW+{CEE4E7^$f&YkX@q(9Y5+q<^TY|LiRKKnT(UGp*Flw&Ekz<7 z4Vn4oldh#10Gl@)7B7$Fy(VZpYaFapM!V9Nq~UfGd8v4YWmB)mw8n4=9H(-s91qp% zhprxBY|C@L8;-8hGF68jp>rZbyr9+Vka1)>z^SUy+$ZBAfg?a@n z35|;*uHHSCQy~P&#&?Zg|NNmma??0}CO$U!gSW62*-f2sH>(RamY<3Z;2A4bdUpFX zM)NVt_NZ{Pz!mj#x+x|GHC`t9j>n~E)L#JwgcuRFNg=F^wRgQzMvJXnU4aq{cV;uM zLj)qvp|)gZTO%_BkBPgOaxn(HdO$D_j@0tbfU0;#UOEH-<^1zEnB4WE0Yuf};doozf@v(>M)E)1flGO% z%xdu8ut)t-E3C=n+z>ngPw`43$Sm?arBqYO#WqSKP_JC1__Es<$;p}5T$ED7yAo2U zRqw)^)kZY8YLRgR?oDn}#L!9J*GOPzYXuu1R_?q$Rt?cGH&1C)P-@z=UE4guBm}CE zcPBTh1|-D{NfpgE1)0#5A`l{yKO+e`CtRx3Tczd*j7bhcCx$yPk6MHlI966n1O|vf z;J)DsChDSCZJX^nxl177l9B*KOCp`1M9t3fp2X+I(i!;@>d#Kn_}vu3KDW!9_1S?A z>EUQJ56v4j#cgn?4X*5|z#)LF;ZxhJ7^pu1@c(bNCJLS9us@vS{^>?_y6lAkmc+Fi z0$(Yx5WG|2|DK-wIxe1yOgq=RU>S&VpFt48Gj;(VQiboS<;LexSQ)JX!qx_Ps`OD` zM-;yj>gEJW><^5)dN}K7h77YJ0(Oz9POCSOS+T6;kccC&8N&@hb zF*wYNUDMrH`Dh1`2YMyGYys;cY@JwA5^>f5<;a#^hia$Do_gRzfIj){XArcI$g)~`*spmRK_kPRL?qo92IP_J4D)2Tuqf9Z zO9Zq-bthih483M4XrIN&UYQLdcR7a-F##mksk0+2yRtSS_y6m(VLY~ub-{oHt@|Ev zp;YGwL}%N|iQk~!#AB9A_^QYQQH8$BW__dZ55YVcm=ipFBuAUONtVtnY9y|_x6FAw z>FjrCl4|0Y0$yah9;EUDUKY$v@HT{QdSb3x(9;4!aQVhk=dbjJ|5eBWAS}YvtsG1? zJ`5(s0#;qvYzHt8bHjM$S%TD>R#SPBS|SMUU2t(!8D?dbptF9#=zr`Hrjjg>p%g~t z4=I`80a_CUkZs5}-;TgW4}qz%>M9I2sEzsoyU?^)~a0sHBVQpwM3w z=_(ir%#>116)B86_xEdoL~qpvcamfhfl%!ePW{w={w6Y?@mwCzaM=NSiuQ?ytwiyDF96q zOci@Wx;EE%dgzp;9EFbf3&gG2!E@fddxbB+0->#XVjZjRFnmEQ7RgwHd=e zRK)(qulj2?k54st2xD&{l6I9sf?QpIF* zu9=MBHiEp!7%loKib^686&CcWpfQC}24t*DbvE_^L&*>luDE#~XA}T?b159nAZaVF zQx#AkQS$>2lqHr9ql4M?0B<$jsqVJ+YA-wMzV{$n-nmU%X+r_iZzQGd8wOHYfs>mg zNFlj4H<4Ea@<7gC7S?@yC&SmU4KbRkjn9uHt%$Hk0RrujM+UsOS*n zjg=_|_7AlvR{J1rYJtEQ^9yA_)_oMJ&_Cw zL|jyqUSwpyU+)c|Nb+^QMVFEVb&ZEgUbExp`+lA!}i$d(N^I*^Pg7l-){?+7Mft6lR+M@k0e4n?L7x-v@ZD zY}G>0uC%`FQ094x$Khx4jaR3@bQCUv&T>^oB+SpTQLa@1oNbFQ+M^74{mk~e@Bvce z`W*77Z}Bi-H&)tQvWqC3^=J%!1QYNY8|$;9H*4<)LMWNY0()z_pm~uq^+lghPIxYr z;i#wsFTcrwrltz8awu}l2(!XCobUc^a2ps7B4|I&^Dnbt##}P=!_KyAmPeGttGy-t zB`d)dm$ySVOQy0tRz26zj<5cNi|)k~V!>(xK|-~o_w`Ns8fx%Pe$bjK2s8DLw)2+( z-hC<~W8;}z9L3p4;8ya)GFJi`s8lYGYp zs_b z-A)^dCguBH>j>}h9vh}X{>jLg4NFr39`E6v!rlelMkPo~Ku?$%@NK|@viD-QOnCK( z^h)k$RYjZ#wW*`8mC>ugp}pOBWv}bjU^62_)(U3&sxz(%nfH#ab<6__PjaH0P|P*E ze#6o|_d||BY2c`jSC_o@-spgOcEIy-60?r9rW_!I4;e8op%d|ni)=TEPHn@(=4{7 zgj()Sx!J5X*ZbLLi?2@sma;!3wYk&=baRo9O17p7f6DMfvEC%66kI$+ir)t7%%ulq z{AB_gJ13ppiQ;)O)wJs3u)+csWZf<{w~?Q7$!pl)6f1(e6Rrt2IbAvlyZx4Le%y^8 zyPhq;%Z&FG(WlVtyEMoT$w>wmH<^r&svZ5#zZ!?gT8ObJX_IGN@1K>U${)=a^fEDY ztR4B$JR%uz_|&ToZh}i(s%IjWC=0mTpVKo6!0Xz-ON=!4$Fp{k;r*b{GL=FNZ1NmV zj>I5Z;`B5Tv3)iF%9GZdJ7Z+TH@nA>J#d2~MPp@!c`c<*jzB&axH10(azViaN#2-t z3zR7pU^T8G+11=dR9XXrbF(5YOi=1i`!(+Qgg(|TF!rNF8XjO8`RzxsFeHeFUbNd4 zgD>_b%1!Hk?mwQc4l2K^ATYLB{}~zndT)!5rSdr?Os_?CUi+zE4eBo#MTs>L$_}py zv6WqYO4S&b4Go6dZmZ}g2t5)9^kSt`TqH;8UGc}$>zk4o{R3{sHKw5ozS#4~tT7_D zgjv3|gklW;r9G5kbjA1MSnVdg$ux~aa$S?me<36`S&d#nAX0o4CIhV6)FeF4#jt2x zQnjP|S|S(U%F@qS1J)t~<9dgRyQ>;wVdrcL;Ta_Q)V^~LO=!ou1Rl`($|e;J!KuZo zsv~48s-Sl15xo4@^3$f=XgqP;F?q&o|8PI`{`|`gANy?hZTZ&ZeeCDV`WoU$PY(XT z7V^rxgJVfG5ZIwLs||VzNz-f8OgP-ys>a|6ggwSdd$Qu5!J+4e$2QGCfke2zw)W(n@61}uu@q&TB#1uOu z24d7yF<_Fc>#y8@v?t3GxNB%p}*!?M&FR7zO2`=ko)_+HPKZxETOzlE@-mANE;cS=9VfY_CMAv{TkZQ&1akvhyOkMX!8Zn4{ z4E{0P5i;;nijC=w(?*#;#K0oa?Rm#W;$)rGOe&(u+q|GqEtcF|ufxQRCIbj&5;~>} zKe~IL63X=&ek1X#O^C%rGx{~Zw}`m9No~q5+@eKU3egu5&V$@#xyr{r24mSJg#~S3 zQ0<+c9CdPXIp4>J%hlC5hTIw%zWll(-+CgRFA^bD0M!B-SwNW55N&6b_Tk=1E2#u^ z^KqWBF`!Sx0Dx#s^hFpaxdrR=R2f;ms;wn?2xZ?I`K6k^Z6w$jh{d9ogw7>^&)3Bw)Ugf<))d`hMvx7VO5 zk0q7GNQ}G{==t$Q?d!z16J0KS(og`N+{+MN5)H}Go^Oorl`s2jWcT+^aLOV!mMy!h z^~NU{TV-)|Sr=OG$aegmsf>A=P+TU01bRv(PV9qa$(%IEWK3W6BLT4M`YQ_YCYbcvqW%3Ma27zNAEpX@FE+P#P6q(O)uY zp&z$3u8wlL>wt5uA4-WJ1H$5;js4xbQyjU*sqA=X^gS3A8X`^`67ofSzc{V}b@mc9 zW}!u1ikmI~qYYW73x@E>(|>#v1GAm8;3%OhczJ>qQqPfu)=+V25x`O;{FOj>aH_^$C+rkVC&c`cs zGC3#$3W2?08T^!~Y!yh(i(e{_5A&^))W-Onv{D$zo5s5l0iO z6VZ?25JNhxcaH=WBn)Uk_L4Yy=3KKHREk&|32t+NLw0xtEo(3HZDW>P&lmD{0r6g@ zkF(o-+t}w}(sNolskyv5K15L8eIWQay_~_a#&LIwca7et<27uuW^*@Gj+YT_zsu0t*VU(xe`2{|mWL;X8Gp z)sZm7m$+^jY(8@3XYv>do$&#Uo;9y`I_#28dZ`12as;>m<~cKeG<7{c8b< zzMmFPdM77Hq9h_Sw2PXHAmWAwXMu3NsppFS6>RY`mx|lN-N~Y|eYJFAy|pkvc8e1) z6}xp#d7jJCRI58u<*s4`lz+1qBuQx-0{-B_{@=T1I2VFQ_~$a+EzZ2dl?h$R!P_|= zLO~(&GLjTs2dp}5S80$?5v1KL7dTokcN0)v58BnGy1>3-D7KuHiRhTpKER$W@FDsu zkTQWMx-df~LD%YQno)@=3HA9QjA|r`6o0Xhb;fD$R{BgvnzjBFA|h(yW1S_dF2Nqg zz-CEyH_{M}Ek0cZ~2~Z zHUA8<3Kfrq)t)|Z94|W8UC7dmB!B}u=t8C2$*+JXdl|n@(y=skEwj;8qPZ>(U;-q| z0i2G2-NDMOV4-rhZn92;?;s6#14}*@yY5Yu=_#ebq;pm|b2E)1=cQ)-y)`do5y2&{ zxxo_L`pSv*vKAE@>b>WB&w|1qKrDqCZxaJ~FA~K~jY3f)QDHvZj%;iG5&10|VJSlU zCTt!vA@XQu*K`!Zr%`_}MB2}X#8Lg&*Xbmd@#s&io zGtwn5AA1;LZ14*Q@9DG@Ly+GCv``SgPf=6*6QzxPnSAy^)r|31RcC zFAg!BHWIrPr5ZF;SwBQ_V}tMpLR>CcQn6O1;Y!Z17ffJ2C_-xi9HO%Z@jN8enhVX+ z+r?{*IDvPB;F?;YB5I^Bg2R)XUFLx--}@5R2cf{bt&*LSR39-q~hNqsje^vN)1=wdIA(6HYH8*r({jCe@M~W_u$Q&M=WD2^kXr-4_8< zM9Z(xN9AqH17=|rQ;-}kM|iCv85x8v?nP2&$5)O`y^rL|=OcAP1%9^MYn~bp?DV+*fJ?Y%qic{nnyvq(=R?W&hE$!E|4OFt zAhM;KqE=q{NWfpq6zy}sDuHXM(mSBwBbNtq_$||;DuwR-k||i@JIc5D$X*~fE=dq} zWs+>lg)57i6Ud#ynn_8f=sJJYA_=#69D|yOh1Y%6GDgoK>+L)K>}A>*joics1RV$B z0gb$C5YXSRbb0lY8NRg>zTs{f^>(0i3VISsnMdq=I40Y`rPbRNQJ#b&*Q#hgV~;(f z7LPOeK9@1s>2`bw1h8IkzwWMq7%!0PpPoJD>NOF=YCWY|7Yp73T%%Y`$7UTd7M2vP z_KjHg)^QNm&e3tJlT5jD@tk1GxG`ZyhE>Eb;hKP`=>r1>P`|2{6QQdu6}I+i#$N2@ zSxuO3O-tQl^^GvtWSG0JhkxI2Dqnv`kg|YSzT=JYpImRt5czh)42gV*lBVU)dy%oV66l`uB3ENTM*$ph@4ABH zo^tINhiCbWcxX)~m)Z*?0-C*;CKJ#+ij{D*X{Eme2bBFkPF`l_=odBBa%n?@JA%>g z*VuRDm8ipMRkz={TR&|4?}lI3mY<^GeqxHb1)^AV3+v^8P6GR*IGAFxjvgfE%e>>) zj#O{7mCF8_@PX<}tHayyKf%dQeUU}i2*STlhp`II0WCGY=DpCU8u&vmM4$W9i<#?f zSi$Ko{BOmVn1&oee9Ub)d2KLB0k%0rh?4=Ju_BkHVP!mG30(@ePxQp{m0Kb5N1fsP zHTFa&q~pt;h_zrTO*3ER3toT8{nKK-(HmKyZ-ro(IZMPSx#GgTz%XH_s}ftf8kGOb zU$`E9m5J#?6+w1o0yT^9dU=rCwOh(1o`NsJ!NL!Q=wFJQ7WyUDvFgpkK1#QgDt&tc z6c2o3rZhDF5dQN-j9(I5i0E5_h%WxjurFt!Gk1*bpUFXwjzGjX<|F9MFJ^2$j|x6s zJ~~F)2wQ{)oyoJO8H2QJ$O$-zxla30XkKbR_|7{3*ls(DnCxA(6$=9qj0IwYL{0?0scgUoY~QO0|_sz2I& z-DMJs`JLyhho|{@p=^YVnS_Px-- zPuQUc+PE&^FvsrJ@myDKLxt^P2k&F!d%7BE2AJoHu3W9)!pB=;8ukUJ*wv-p51Eb= z@o}AvsA-lOOpV>#%9y}d#x^dzyqI)x4^?WWe-|gAWOKSQSKzh3kkGFvDkk=bwZYU7 zj+*isouI=Pdn3l;76sW3rClIgR0}OykUemi$4MWbfswf36`ll(Lm!cq0MXWbVN}A@e6T>R<SC+b=e*yM&o`@2-w9+KxQ>T{U_--2~%So@B z%KSHD(x#904k!|c$n??+b|u!}b-Ju+37+PYNQ2%cznMep8U2?Dz`*Sd%vc0ASdfUZ zY0$KlArzZAe~#jxNQA0BdP=N%v=jL*9teaso3W@o4hYNw0wUP#XK6iTq^;wdqAc=? zaJo_El3;lDgVl>!zs>PK5N#_KLmb^@YdcLS3^j=j>CiZ0s}76=UQN^yD?-nJWd$jb z)SMg;4-O`r5G+&l>Bv|&-{;GVi18Xj235ubK^bqH^Mo5~N*kRm_&nLnC95?-Hxni8 zi;J7c_wjw(o0;l3=symy3ifYV0(8pHFt62woMicTlc$uy?xa}fBc5g zzB!rC4{c>MS=TvB z2X3Sc#b+#fy5{({!fvH-mnW!nB8cLu1h5|fAGF(5ww3m`F(p*2jC^4F0o`Yd~?#U?+>}dCJ*C@tFTC~3jowsd2MWp%8Hrb26+w8n;b7k zp3DcyqPPeXvOcmn9)Hzzx^m#|Xcu(XIs!r0s?Sh4q9vwhQS)*#eGg<(Bst!y4Bzc2f2;ut4Za%e;yx@{ z7B{zKy&}S~ZAb8K{F_vOKO`G?RZe4ECWy+c6>yJ^#gu0ue{K68`f!{ zG_4H6uFAZHz7r$Xa7vf-n!`&GB-9f8-11+6`HfGVu1qFt@XB0y40%4!VG@|Rc zStI6_jYpFdf3nq^0q8c^{&Cxi)G*Z$amR{VrF;3L@jUjnc*S`H{7;OzS{u{k<>O|8Y|A&8PC>Zbienk&fp@ehQqOvhT@XK z0wiu07P67A&6Vgxk6bz)$niqsg?h#uT*wRe%0#EaNfItZFasL#{$5TR$V>797rzKI zbChfgeALd@)(%?@NBn$sG~^dbC+pJQXcD8HBRkLh-z)|03jcv%Sow4X3)_PpzX;c^ zM4Kq=g(!OXQS29<{PEQcw-D@99KtyQEyVq>{!lmV!uwNVcSv^25gV1}c;kHA>ZYG2 z@XQc(F`4P-lmJ@ef}t#LoW>L^4`e&|sose&b>?dUb!{)17sA8*kHftGNS=u^Ag_k=Zf(Gp7&BSxU5@7zLTqATvx4%Yn=XHQK0 z5ot!MT}mQ~#|;IZY;Zz>(c=$@x{O%>0VwO~3tM?J>}f+OwfO5}>JF05_3$W-@c#_O zz~y=h1^k1}53NI|1so$m{lYQ|#1~#17{uJ-R!={vtuWZp^LR$BTz1z~kq6<8Z#SYO zG&`WW2w!*ji^~42jEEI+!<8j;7pQP!^8T#BSH0a!V#bz9*JW4e?rx4&Md34L-O$1l08!OkZ*3WS&~>% z#CirCwCsic1s*A1N{dUB41d~-%b+1gENSbBQnTH$U1{TFeD~q}PMa^!S29K&m-+QR z6RF%z6kBc~lAq;1R>!M7Be2+gANpbZ^H+cQd8eEd_ZQj7?BMSgE%xt8X{HB6>}bNm z7foI17-aqNHP%2G0KmVhMbd0a>e1ft>4S6V6K-&vN#GN?EKY2g{VE zZ;j!<+=p-*SxVoD49hRblXQdR_=YzlwV-}|@Nsq+{kO=bI4~;ES1xU-vuc0?OwOc&+MB!%5iw!H9A6o+Q)j=U75IJ+8>n;^XzE!BCLE%LLe7@D z)SlS9rpsXd*uECc?!|*+6}USy`eg9dg@Qq;5Ef*Lzi0U0PVeMS$?y?7{rihNrNXpO z6B)$pK@>WkWe)}p*ETM->P?bFzbL~m3=J`@8*TK9vaEm;lzSVL7Ha7^*#dSi zzsggWrbvZif%Q@vpeic4%C#nU4J(tX`P%XGRn6P-tcx|Z?lpNvkn9e`w@Z{#&2 z;g66dCB#l9HmT~P6N6>HF+x7ma7|S^W`3mkB52=2j_K7zsRIMnnw?=8!^=mJ4N~9~ zFeIWRrt4NY48i1wi`{@SNxXnDbBx19iC~iand!WF!eig+PvO8)J<}sxnG1J=XKD!k z%+cz07Qr!UG?MR^;wy~%R)zEtr3&`{piBQ&#pmI7V z7foW4K0pya2sSv?7~X%1ofO;kZVe`QfA+C)ensyly$9S#)?bWi*!yYPmAx$`5x8xO zE`4--ullu!zgtZGF%0Q6}lx3X5m2R}eg0tpx1`P**xQyry13Md2eXk$RIaz|-P#9dZxGVTa4?UjikA|UhogE+V z7>@J8M297T(eSd<+1b(cH^&t`n4Y@?H6_wue3d_i@G8vF3J5HQW{s&=}}K_ej0b|k6f$xZ#sJN)a5!%)1-V*|&!WfTdy$TX`dzD_*J#77tX#WqJgkTD zEHwmUlu$IN0tpz_CE$K3ONMer0q9_2a2vY9K5ursd9fDaAs7bZM^j^9r=hxogcp7Q z(*^oUm!CP4jC%4|Nxdi$hchn%U{Hh^sryF@B}2ITuhDP-EO z47G%uzHVwN9as2TG;?Q$2{}CTMh^7u$N}_P**#`Z*2U!5FW9}7Fmn&!>!RIb7=7i$ zx&G6B_b~f(I}WmfLk<|c;0zV~oBW_7blut!fB2k*5e-xoBQE-><>SY{7BS_J$V9RK zJ6&=S`GxbJZze~FJZ)s!C*h%M0c#$)Qg4d%BIZ=9f`Ege*(wY;1cj6rfg(bWVA>i) zxZ#w$&a!KMlUqxt(Zu#c^$gCXgXXMgV+2UWp=xP0>QRW35&fmumBa}-7PEp+=Y{Rk zB@@`|67*ZIMGP|AY=2BNGo!jADH>!m3j6oJpAm@s6Gv$FpL|M;Po)RYpKie%b5}H* zIP@P{T8g!*t%UA!d(-QD@NyAOQ`F|)5{EGu15gn0?=unUCpc)6_)M|tIowNUMqPm9 zwd{xFrKq2}$^MEpUY)FBfBQ0I&wN1M3E0G`i$QGxc{xLn!SXRp(C+<=<)5s8gUSAL z#cVpwQ7a^t^P-)A0hXdukXC&b3aIg|r$rBYi)FJ6Ieu?_%`I@n9t7zPzZ1lm*n zz9}wt?h7S!?%u_p3AdZ9uCnDSYRt9P=A{#u(Wou%t40B9W$_IRQ}=EaYu+%fg+P}> z6hU;ijjH!!2x;`&SyzNF7n6=x>9l046s^4=R!tVt>@tB8JD2hHR#i@VD$4*0J6Bo8 zs&OW^3)|b;8Kg`fbnHX4iUCuMkN4kxPc|&65cuc@B|ic*6SWEEJ(-9Y6G;RKCOfaj z0AGHr3CEQ57|A*qdYF~)7r%QheS7aa;cb!ij1Dbr{1L!zdRZ@~W~>gB$rGww7nS-C z2FLdGk4jF;mRA4jmHER4I5C(`QTf1A%A2`Nn>}+(+)XI18? z{sr)RFRW;|oS_&b4i>*1$23&ThWq7}u#TP4Lj{gcByi>v1u2*S5Rph4X$CeT$@GO5 zeQL}Bj|3PEZ!20ad=5cpXmsdlm-R*s$zM`a zcC!6Gydq5oi4rzT6;r0B{W+OSGqt7|RUl*v82aXms$-Bdy|NAi?R3fd@%oK)fmpfB z3T6g~Gws066=6Whr0s705_1i^Zxb`cj5!<87J)VL%w_$vxFgP_)J(kBc#-Q(7kQW0 zAzaiH7i@oO%^f=M)ci3dv`+sD$E_~F#;Lq@)&J)=!;rhXYh{3@esVOg)x2rRf1nJ% z3Ez_CkAyzN4qbdA)*)cP>e0jECCJaX;k@hrQbgh%Jrj3%@q;xd82o$K*4Ych60?&R zru=%_QO%xb31OU*s^gKX$};+^ipV^GrVi>?VBv~L7=jj?U)PZ4@x{0l5!0_dWu4e+ zG;i4#MI>5#XSmjLDEM#lLEMi!*Ljx&@Ch{`DiSoV%$Pcwa}2qd8Ks~E-p?vn325%B zz4;ViL1{IejWpy@2KrZTMQ7<}yU{&?P%V2hvmKmEbU*tV3VwC?gMGv!AjR7l!>_J; zeL~6|oCD6$KlUf?BZcWe9Xl9fMj=>KGOP{kCo)Ba4LJP~F5<9YoS(0M&_oQbw2QS_ zIgbot(}jfv&`IqosCT8@KbUwl;}F0X=3{`i4iyC25B?{PZ(3$AkENzsdjsCDgU7Gl zm@vGMbk`A%gM}CfTR<{ZNLK_1jzE7q6m>D)X#|l6YwXN?Hi$IdL0evU?rwnE#r$A) zoe=V_2lvcqKb#HqmH_o%e3q=B5BhhBZncE1rHJx?-pEPOBU=QrmtG_secT`mR(z^* zsYHj($ez=bGsO0jy%M?a5mr=1G6_lU4#xU-?!ueaHuE!)HXVjBx{zlfY1LLEHH7LG zCoEPkUG1cI801=d1NQ5rfuckP9OWbNe^_=aFxo$pR)W_-+?zqe;A8(GRLKjVbX^gm zR4b`8{nd?r4Ds?s!m}soE;^W&;n233Y?d;>g&g7Yz;If8zB8^7nMU*S@VqKrZh7s;Fx zLZXVAa==#@oI-=yGo!jGA)U7ewWsFyZ9R=Ziz~2>7klPWpdvM13V2&1DYp=qeHktO z<~7X{xF5V(qnKFrWpitK*Cs2{EXsR{J5Q8^#Pa7e99i@&55K+=G=>P28<|Z^0gg+z=bXKp4r4v(gTJN%?D!vEj#F$06J`DN z(HAV{NS+=c5{TWfTSs60Bw|O{AwfBC`n=8_aOXUVX>f#Zx84Eq@lp& zt8ojG19;A>vQXp;#Th0aaTeIw}`KEo{=35*d~PC13Nz{ ziV|H~O6cegD-QCqa~!Z7i$Vo+t?kudi^gQ$qG_}pUUP(_~Bn=&6B3A3U_T&()%%7+bVSV6g<$w zvjvbPU24)8?V4O_hgZ*_{P52;%~r|B1{+?twghY&5s%a`EosJPbZUNG$_o%WI{7g8 z|5x_*?Fkr%$6KkZS``QuHUFzQ1u3NWEad#YM`KmmT*2FIuvZZF>JgCZp= z%MVP^{8hps&MY~CEKkt35d;hAJhQ1010_3Z^xQ=D+Eqsqig2Q!!y$5yEqIOmhJyP4 z>o4nAVWeWZ)j_XM_P>Q;0FT-_1s8%L$}NjHMH5Bx#;OkqZ{8A6$XOYR{=h2^e}d7a z+M+saooWD*YR^NiVN5fW61C7v>c?nm{4e9iX58a|3yM6TY7zKUMWfh2g?c(lzkn%Y zk<2%(!jvX`l__-e6WXns!i|jtw2OHo1xZ%0O)Z$t$w=hCX{cEjt{>{G$q0aJZiv8p zIV-sj);yPC73qOzcqv<3C7GrR{y;3pX!?x)Dh+)r(I1PN(tA8*tVYteG6a+&-+^u^ zd3isQ1fhWHb=q;`yq?WmE!~eAhI2}g>Gzcosc%sC$SUdr(lLsk;p!W*n|rl!TU-KO z159&*El1HhovL8JFo^!qMevD2%P-*H`*G<)~{To>;e}szmV=EJ*gAG94v5Flx(1?{|0j^2QXCn|e z7^DWgf@j2^E?d)8-rIF^Q~!tYhiv?^x2kQwTL6%c82nF-(g$&v5Jd~{RM%Ghyta`3 zr_QyX=)Tq`2j2z(C1mVH3Je)5w)IkO&9yp$e2$?p%5S; z)>GDGN>e%_HwPi~xj88mcJZiH%B$EPpfWcVohCV03Pq|Zu+xVTeCZ42sa954HJmxI z%ztLJj2j&MAmd<}aU|&Jxw*yg&|6aKfgbBL7!rgdkTWq*=fnQeN|?4wd(8XV_cv{* zEhu!Y|0}u<&8`F=#M*CbhH7)Iy`Vkx2%YzwenlfGgHDEV81Iaakxyz;-HU%GeA3x6 zu1qjsL@&Wk4J)r4gb=eZX=+N)3oK>DKU2)~S+HKXNPeetaQucp0&K05(wkVobw#zx zq`h=l^M1t*T8)6ziZTcm6Cc=F=a@>He}}lO=!rX?z0gO(8PTXcb9;x*NsBPoog_OG z4WIZxmA5b%Ur`^P5^%+cWa)86rOePUaI=pS&CmQV8W|GqM1cfBk({ooYZq3mt;6Tn zH~T57F+Mjn2S!12GKi6*5M5gQ_-TQe20zWqP({#$6><}Z_q#yjNOCtpm;90(DSKQ| z6Z>a43ebgK!9HD3naUJ^64TQyx(WC(!W4V2*fjqdECvS%iQ(mj|IdlD5^stl9OT~);_q8vVL537+Ph3Ck5F;yS%h5N|e`w1n~91 zvtOJc>(7c06=_(=n5@Sm;fSN-i9T4*E1MEY?3MjxQ1IBH^9kdEBL$;L1&X^sQa5X; zu=l*}94s(=NzJOkr>90<4m@W#AMG(f84A%T99vQr;R7a!<;%{35HfmS=BnuSLTPa$ z*iBGm8W8Iss^6wX7@>6Qa6coBgNg3`<6kV;1v*i~e>Ba-7R?X&byNL|CJ`d%TG)WQ zQ`eJ85g$anP6s)}usSnMCOY*AWX;-c(r0QFm+v)$5%lE%i*G~lNvdq)!+if&$9W%R zZGHmJ-Cz7pJBL{qCy9Qp>@l~?-ck?Hw2B3h5A*OXq3<0D$4U&|Vxi1ALlVoydPn@O z;|`zi>a5@d!SqK+;&}|*d_#ab%B|c&5+!lI$9&TjjdOq;WHb|%s1P{Z;0{h7IwOe5 z@8l_ztivI=(ECA!myrR7^eg@r4G>pev&wTs`GjRCgoIjnzQc66tNQ@!QLopwP|vGV zrA^5u_I}UZ#=|>Q@&K|#6O1t=vAJk92ztj8!Zd``n2Y6IE{iCtoD$eXVz1tQE|F^! zzPWI#S}muW4|-+<4YOB9jVSWMA_7h~MdlfEHt`MObed_;-a=b zU$C@O25jItYRTpl3|;{hvV656SMIyD-F^ZBVHe^3jL#)XiM}TAYnNfl53lne<}#ZM z7)*8L!zN;%v2?t({5w~RFWbok4vqcL)l!6eZx@Iq^wCAq58M^aC5Z?jrPQQD5H@^V zG0ip%Jm72fv0LNjP_Y?yKS$22(*V=10y?X7&AES1ZUTY&f69iX@tYX}0uVGRd-kJR zmf^NpRd_nb)ilkXv?z9=<7|HIp&4jbgOR*izgfGu>p4!1`xO(X0@55w?76671^D*1 zkjC!v7k_5=e#1~T7+K{d>>3pUp=GU|IU~lizwtj_TfSYD5Oo-%s)82e`TJ-Xf7tR_ zd8xF(mE6U*=ggBTwMy_@m~--wxn;hbziVyt!SN#h5-yLCxZEz6NEUaLsBCjl+Y98P zlN1-LiP5hn-^p9P^U)~q5qqJw+ZG0?$y}vN2%&H;ycHJfTG~uA-?iBf>j)t#5@y*s zw#+)GB4iXv6};hl1U?tO8#4W(8I8H{1sH>YXES|k#N&UHlz;mYOMw2+eWw1$fjb1o$%*4gTO(^&W zfOyI&m`7|dbnt2N@cICF5fPJw;e{w1)X^E0aNwrxOlJ(gvQdc~W?$dZN|z~ZQsajg zeS2qh0FE7ETFlp04pxc9x9&CMwRLjgL~|Er>lg*v33TXOKrx6(ZHhNP`2_!B$sT{Q zf4kw*NAl9Y1n7|obaOdE1M-j(uHVC@TqTS5k9S2%in4SjH|0N5dt%^SQ44KL;_pPE z*I^E71kZ}9U$181PD1J?eqUMi=5m}D1)O-#$9%UnLVUnwEz(>vD z0i%kohmvvat+`T$w8IYtf{&QgrE|0Ag1K@DKsJVJ(3i4#7fV z9BpdRE7m`mS72axVZwTB52w&^y7Y+&wR%-=U zV1Kjv3Gea|kyvBI-env}#J$6wm$kaEV$PQJ7qmxl2yZI4F)Wrz1a%aV@%KS}od(7J z>S388!0(o46(n2%(*3f#A;YtDDBb~lQAYO!=QUo}LjgNM74j+SIOaYs?h_eI?S^83 zp@V+kg=lg`B!A`|RALXm64f|Ab0z_Qe{Rc`iMZ6Ll$Hux@}(NN2(E`e}x z2|A-HAi91c$8rzASo~p;gC6>AK(!A1hIwNt zzG4;i@Kwr$zW>-8fL<*Q@ju^gKFcOnFn7*FI<3*sNs(n5v zurKrJ)Nz76Aj#umnsyio2NGli}nheQ`h)%?1-`%yeP8FCtgJru3@^@kIpGg1qeK88`PZ) z|0PtYB4~j36Sg4=afpgJinI@VhckEqjzO+CMAIRtA8o1j6nM2zcCsJ!=#cE{SC9s~ z^Zo?K1zT=DYSjtacJ$7|E6N^VW%LfSXW&S>w^F+1>25Zsf@k1*d((1R?QJ{d2&guT z47hQLN_kk*ZYsGy8U0NXsb*Gn3>OKqVo-P90VxOdJy4cQfgM`fMs})h5uvqiJA~~r zj~(qj4tc1at(RN~r%jAjc0?_}1s58w`5amS<2Q{y*^JqfRmnlJ6S3Ef0A2s@bUtpy z^wtMfo^-9S5x8xNcx-Ghx)u4TJ-9I;>f6o?xLasiD5FHRuLdWDjtv*AEDPF9x zJ;Eq>nCjiA-qyB53kQdBja8C1plrAgpdA(f~5_7$S00Ot%_7So7ZFZ6yr z(<$}wO724k!K`F;krL1ax)&mA2}VsF+|wbhL}-}=+ZY1FtBe{H9?$4&VXlGLj<=QH z0VFBkwXuDim#iXDI>qSh8MWesbuDfEXDpeG8+&&Y>0r#&KdaB({V{GGMDKx@D6)4p z1M|}PrUZfnS4eEZ{T+F&;J^5216;5t~Lx)BZQT7*%d) zchI7`Steg8j+)e&#ldKu4qM?gcb3gn^7tuN#Jl@xA;&s|3+abswkCih)h5ht=%3{6 zTl&VcQ!WYSX}>+MZz_lCn`R0f2XXl9#VCYB@5be35o(23J=FgqnXZxjKW?)LA9sVz zmorkatM&s#dJD0w6=q z7CsP!V+Z}wY`6*@K9!lSq|bSd!YcP!2{*}F;>|f6iEv^PR8{$ppQ?GsILXc zg}D!`@e|`o{>m19b@;VT2bMUd@;Rd1X_7tK@T&3a>`FgS30KMFW-1^JW`kthU8dPu z16Pj)WdfYEe1=L&1u&u0g+5daUq(qcZ5RB!C2QR13()_XMa5_J_`wu&Vzae?CpUVh zoj0jAaEMe)1Jf)}!v3wGgr`$G4xYXc($Zngt8Wfc(VO>QBuE~3J+!p*SFPA9z3gE& zR4_8M4m@pokt#%FT&)ilk{IK#{W?>65J_jFBrTE9kl>RRkiCET0UULT7xd$TE^DE! z2#7?T^+|t|mOI^Is17@N+2MZC?1%MjMB&@jKrZ9-dP%bFs6ZTOB2-Arg-*a=4*=lM zKlt@O*H&bm80K;_D&S(#u`O~DjvW|=ytxoPnf++f+20S>Q@3@Wwwg493dE8S?INuAMr`(7QoL)H)haD`uz;KWcK>D#a~Uwc~9IhZv>~G3Fx>aONwwZ;K-}& z<=fHU1Qu$))JV$(Pw7Dv4p+WQHg6``ogtnZODKsP=Kzp=3zJR#EuhM3+Hh&_va*0T z-`fK^JW|brr8&;kr~w&r8{|h?M9%jkL?sqIT6ow#+`pUVN7U^1uP)pc~pjGcb{+)=yakLxm7CMi6T~Y9$E=~rHYhRV9W!B!Oh@W0e z)YJiQJt0afZg_(J0$Efn0qO3m9<&3b74@psq=<|m4g22^NHre*l**MHWeA)M$|*ad zs?k5C+D%}PdurdB(GIxTG#}^x1efh44euewr2U$l^j=`JtTS&C;7p7>NC=`c0>M@| z__4Zdm!Gfoi|rfgUeaEx40fqIq{0G$*9i!8%r17b=v^r3rQT(UaifWV+fwJ*W&#qH`A{9+pkat}A^GO+C=P z0f%}#AhEAV0M(Q}I^FE7UDg4`Wlz8>OwUA+#(@+rJLl??Icpf!1M9Cq_UO9uoO?jA zm?G}y8NsQ%A>C=s0o-~@)Zdy0P!LvDj3Tem(r;I>5}%KM zYxx5!iJ`Zyl|3DVvYJ--o`Dw4XKpjW23WPqZ!;Xq1q*hk_cI$3_{eTv33ZJwYI)IV zI#aI}bpg{ZmFj3cwbSk(o4Jr1|G)SZ9+^p&vt%9AAD%@m!sjkn=`$2&jO2ItNOTGChS$Bgs(D zSQqW9Xule5WBUe2c7LHDyE|)&c}+;ERr)-AD0=1{OO;8cj%wf?Dynp|alw%B(WLU* z7e$!IHtgp~8<9?HPL?B8MvVnt25t=~53mb2*duP!YP%+KWdWjEYof|(o;n1Uz3T}P z8*_!c#rh^FvLNevL|Gnj2z3`>PUDY;o!v7tBaK$P-i%dG0aR%ZIYH_hFz*F5Q7Dk5 z%{LQ`@ZyTNaI=3g4zPh%kfo$H0NzeqOMvPQl;+ z_f8eNwB3!=1?$+q0Dy%go^34ry9z>=F$(R$)lyPT1KXdSY9myr=%lm=^--*;XNgQM zy}&A!3vB1;WyBHxGaz2K#R}k`^i?5q*%N_C3;6z0&eRl1@J7y$fc0r6nlJ(;*bB$e z7`i9HGfNmhs;j&CG-WKIqjhx%FM0=iRqBgClrh4wfgrVQJay1;BO4h1W~H`51KN}i zR6w-BBrfJyr^?v;h1te9Hg1l(EGJu$Bwn2jirht173z2i@d}&4nyp5y7E?Agj}W2E zWIj3DB1Oed$qV(r-&Icdzs^rfWIg8F$rNP8-warWuI{}A;oT!N08@FDK_{kRuVq5o znmzz~m6F<&3;eF-j>V)Mi3#T-g1z=9MZ?pw*;J#h)AAAz1@p3s_&LCnerUL{&_4{u zQ{dSEU_V9Nxz6Cv8H=^9JZ}CK%d*mq79yukI&WIq6L^ir^rnT5^6-BO^F?o`$9i)D z(I7f3Z43T9c{<9g#>yGA#I5i(U#HgL7vI|<5J)*1*bCD%JtzxvFFt-{D~CN2N$&Oe z7Ud@8d=}*Wxu9wQdw#4xA`l%8itMHd4wKlXbrAn%Qd&oTojmAE~R>;74G z+%+R;()Uwf+|!+Tgdh!GRn@(Z1J-kswe7_qEhMELPgl&+bqv$upt&`BgBQM)MN4F` z2mu|{(2#r_Ch?5nL8FyV8RE`*a1cg%G6AE&u`FUKo-##$e1HM6y5OhUeyN(Uxg??0C26W#4g_G#Ffz(vE~Xelk8 zjmSxBU^3u*TbA;~rh%CL@G*YW6e7r^fCz!r^k@#K3RKg0QUSD%7bfp>>f)`Uh5KtM zhOJs{t}+i^8sR3r5bB%j-$~HcHXx6Do+BSo7FuUwpn1TeWlr82PtX%E8zqx!!ajfC zf4n*JyfNOaKH*IP9^PF4shfj7Md@0jOl1zeY-J&!53d!pM1Z&EA z%0ihP%(p-FYu1U=$ZQ$0g~^VY40|+-ETg=_YkwLT>$K@Kg|MGM`>T^^)s*{)U!5L; za%l3wv#q7Ik5yedlB0I{5%SgWv4c-M8n0tjILP5+wNbKw* z;Pjas2nZ#Q5rmk^rRIk`qH}$wBESFPP3QZ`>JHEFEm2_h4~!E{%<#l zqY8ln3RtA~TOr!KKP^5cHw|er&K^w0)V_hWGi42FqnI+9l<-j!oyLc_fY~E}E8H(| z0i-gv5_XU?6fJBCbtauAX$RxaRueXNwG87iWKwOKmX!M~scIIcFm7&<#R7|G7C$%{ z(B@4dKUbB$V5g4-e4N(61jm1%HMsnI!!&R*2=~hoR(&3O3VMOSXC$FY7;R|eAlyuz zdYYu9Ra&@b|EV>h$jb`SG`fgB73J=39Sg6J-U~mrGTJW(lT%LO2cFI5$CL(@8&%ut zNIOd5S%r%0`M6+uy=W6fZXU)3{7L+l*Q66rZ_0~BP`SoP+$yz0my zP8){TO~ut2+239u*f@Ow%(f(}lX`U1AV3)5(OIlK6RnYhDnkX(z}efzbqIg=l`g6$ zg^l}089Hump}~1=6c6Ph6iv&9S}trRpw4l{6dyC~=@HF$Zd+l!dHGNSV?U-JWPSA- z`vKbXcXgv!m+?gQrwd;sO&NL@N3LAVhbX5c%rDl%BH3siCNiEo5;8d`!u|?1P zQj3sYAyQn+S<+?REPKxevi)=e1kBBYN6gU~a;!2+Tm5y%x{Q8;9~FHaP701;xMjpA zlQr9tZ%=xuP}%w^)Sct^4eS?`lpe=f+Y~Ny`4Ur3cB5asiDLA9x+`S=4`qO4)MfZzAnRArR^exAFJhCH?QRniU zMv>7mtFv`86|M^}(WC}au1NTuRXk~gRNI$go%$)Z5Ef=-r1j6?nb&`#WbZ|WjNc@J zwL2Qy3XEwU(ZmNbRL`r}t_Q>$tz(q^{)jH1rkZhVSB}si*qEh)L7(!~7c6nZwf=Is z4=X+A_&q&R-A9_`7`nb)f@n&B^(aJ(e>nkKREh5ykAZelfp&E^^TIX6WA~!NoqZYKqax>3${X+j% z2mO*(&^eV>Q;gt;L9pf}Es4fF#P@M?mI1?#X9^YPb!J{dpAV5j{AYOR zATiOK$9aW5!BUhs0I@g5nW^{WgWpkxr?kFz7s(iEsucJnl{e+qv?SK*W`PgMAx^c` zT)FDajmS`iV^3{WG?Jje2>9smXcxYfk)j9JR|rl>a+H+^_9{Mr=9W(lIb%D@2`E$; zN>B@`rb2(Zo3O7Uon(lTj{E+Ko;k+watYfeB`)@<(jP5@Hf)bISP87-a70cCg$4NX4wot-2zE4G03J{oWwsJRJ8BCNR$Q;V;1{sI8`aOQo%xil20q z6QV`rc(4VJjr9zZgU}8PAqN?gvlV?Mfz}9HH*xo%Ht%0b<0IA z#AnJcY=%A+icDZJwhI6IUl(2Zx1?$>SrrXQrgP@|)_$qcfjPSvVLbb}n#tH~Bi|Cm znkLkAH-JVy*aHGdcP~n&tL#tGZ!U;SNXXSjw_xncAS-PvG-U1``y|17S`9FGpMnCL zA}UY)DHZ@c%ycpLyrf&JB>#(l}78LG% zG6`ej0Buqw%L{^5bv&5~r8EgN5b1Wz-bViDF`jA_2#dSS|Cm$e;0Uc?fsW9EmJ+du zhSFmG;l>?HIs3e3kh6kIgGfp7Z!(vwpUXzsw|-#aonD4^PT>$f7-OJJ#}f70BNWcmuN;fWDl)KdHY!L*?)bM zxmo%(dvddjGwc>HtUE7m4DC&-*8SX3U{S@v^T7gT5#DUhVH4EX-Yx#A=?Pl1r>8OX zvB1`)FNaWKfRv{o8uZijHq5%)ZpL*Em3gAa6H^e`)C%S_>H@O1)_myNQ|Ovn{?nez znUr$&oZN8U=qy25w#|N~L%_YyG5){`0IczgOAJxVp$w$W##`6einSC+bW1zMxA#zp zxeMb=o?Ve7q)z&4oW%cNWLsS%P4+ad%MkuZJ zk9jmk{gv9*gB%xWQ2+DDDPDH*=on8GDPG+I4J1IOAE6w;V~1`sLZ-34*FpmWnl6f3 z<|bx^CfG5^ge5v87VR^i4~ho@+yJq+ zs|8?LYgp5;5xQJzlUW-YHK$__WMzs4;)p8>jl0t`>3F? z+>j08pb{$6wx5rTS&{1dsqgj%azv{>a{mH3!9xUY>C(Ac?7|mH@3udz_62klHef6dhHSILNCs|Xs&)DpBDSv4|%dD8Sq5YI4Uzh`0kMci9q(3i;ffYr0k z>}AJcdf4cE5_um7T`7648X477MnfMI(ymKs;>@Tk%ZZbkg zmPq=qQ^j>H+ztZaQ5wMdb@|veTOllzO=RBF+651&T8 zdIZ>VQoD^TzBjgVV3XCX@~1$7w7u8PDgG&oIhET-o&o@sI?rMwmpXc9F+WBX_d$CO2Y35B8FcDe)P~FaB9fM-ZLOcX{f; z^G3lM0J-;9{|l`3`u=%K+uFA-AWE)tEAXyMKM2zwY}QQSSp0S$$OCC=hF}}b>KOKP z4BRI`Qv^qdAotqR7>+m+TXq>$H!3r>j(39tqj_*Oe>w8TQ!uJr_@yj`nYPfIxi05 z;0{S)d;QbW_pbQ&8I^EaiiI!ip`;JYV+Ch-qh86K6}vovu33D+Lo=hp%N)39ooH=c zN5?i4&Rb7X0`TTgs7M znKmASStR)7aGkgtr*J@$WEVr}^F*qH+rOi|~f!4a6y*{v%RRE#3%0~Sm$K&g*rFNbCQzWr1+09y-i*~<9L1Qc>N z3B^c1q7uUSIS13lHpV?B4>9t6+x;byZ}|c^E?XQi=(8DWh3Ri)y=W#Q*K#4+MTkNM4MERmefoUymN& z7U;ITAGx8`Ab4F=t0pz#;g9k)G|e(g$Hu|=ygCR}Jru{quFRBXeh))TeLc_4;E2Tz`V-z}=MNE%cnJ@?o1mtTWew+Ssxi$Z;#GT@&!V zp99bCQPmJ&KeJ!N+_*1ytLts|gA@tr@U_!1)xB%!wWb%-sA@|suP0r*2?9mv8U|7O zn9x?nIDgj3)?!g;_@(??T4ju;pi&$u;D#B!OIK3q@Ds-h+fuy;Cd}2<(H!d} zYu5fC7Ewu2RDyQE>?-7+#J`)G^az33lLH;X`6x;qT1i_yz7C-(>Gf&~lV%FzHkgmE3RJY?ffm zLc8w6iF>gm8O4*fd4CDiMD>39r2u=rSO6JrFZ5KDFi5@jKG!X`|8n_X!_4|IP(Ggm zSJw#!U+ojD-qK`}a@&yo+ehR>@(vOI{>nIdftyN`h^2E~Q}LouXcuGc{5Fh3zc6;F z(*8qIk_?T1OiKK&H>ehZbr0|9lT-+&ErHf0DYG7duZs+6Zo6Ve|G_&%OHhz?LP$Wg)Z_MVLQ{mt8AyST4tNGt??CGhcGa-u^f&CG?@-}B+4Fad$2 z_W<;a5eg)dg$=<^$Ivkj+d})33k}ErQbAx_(TgX3S9KY7%`PiQhkuXDA_cu_{o0Lq z#_7XKr-#pydZRJMa1-)|+ASnVl+k#+WDkNj5=ErN0IMOcDicT0r7B9T7P`t^B9}vIWg#QRdu(l7eQJa|& za8{NCQdYrw*3(N)S%6tU6zOAXj@I>iB{|qiSFkAFR1gxNR_T)IsoGG0#>_rj^n?DD zl1*5>Kz3=uXXYDykMJtRSYLA}{QoWZA6g4QRSF495Y(QWB7>96AHgg8Xiol?v#AF+ ztua5QdS?HnN=V-=UbUZF^PYt^XnJ&sOjLH z_~1nB2dmq09P27vM(gi=flaMPQ3BE#Fw@aObi7-`2dS$YywSTWv~`uLu;ueCZ1D|3 z*_)L*yoFm7f`vj7#Cn*oISv|stz{dpzPbZ?zDNFBGrHTZ^2#zrfca=`>vK7aWL@dV zAXfa9d<0h-YhH6Dvk=kS@!Ie_AS03K0R;NNUPu~FJ^rWl#OjFnF*Dqn zN$56y^c2(l-~lhxjiQpd^ib+m@AU$Y;$(hY?Nwsz)F&f%r_A|IauBhX5}(CENOg`+ zY%S8KF$;Rp&Y;fy8e4w#%xw*G=dQ<8EQO6ZXMH=urY{$kVhD76?~^L6H^cKvPJL*g29K5EvkPkC$rfxp z^O2mqR*sr|O|vj=l>eKjI^ovn_NU?!Ffp5m#v=kr`e$O6(kc%Ftj;d(gEQN7j>>`LJ9S8P#+iIw(Hd;lb;IRiMXF$>);tC*fY-tTLo;<{?le)*GA zK}F;>MwO65-qcK@+tp}ZogFjtrVmTq=CFggnQMjT2p&bvZ&M^MTRU{chD?jQ1I_E; zaAcVi;1i-NMyoADJpLZ}o)nVq&pfnb)=wc!O=YB_Ea4LG*I<^pG|KhqDc~amE`!ZB zcvH^JNugCZ)aqs7>j9RM3t-X=XK*%b(qghe`)z0DKd;9)w{-r%+goYI)1S=$8J!u+ zE`2}-bK6Z#sRU#96gYjC=^Z9mAD7D!`_jMBsNR8pQVIP|vZKJq*(!p! z*uW|Tf=2gBnZGD`nsJ@YohsAv%_ zB?-q^5c2VFRciMlnldiX@}UwS6W>Dh*8%iaH!*!Xh@e|*OF7~q@%{Xjm+zpSr^M;N zL4VAJ>BO6j=GH0)Vs4XqLy9)?o((3x*E3@iFg{Mkh=iT6%X54$1-2tS)_UTJeze|9 z3+r7jI?p~8pYZI3-Y$I3-vsDk;6c*KECAAkM{#Kj$x+uc(gs^5;>dA|F@{4-cM~w{w5N;1=TV$9$9LxQGIvP z!2_sjj;Z}DC29S(9+h|WvpJ4S{!Nkk=AMo^XjxxPZtricP;#w&l|YIle;kJ=tO4dj zW}EqM2f#+N=?fWJa0PKif2_LWuv+NI37#fiKLJohp6o!u@hJ;7j>J!%^#)2N&A)xs zADgT2HZSk2DtZ#YDuRQjrGZ*nyo#qq8;56I$ zw|-KxC(XjnntLfCUCe8Y%F6W=RIL0cpl%IsM1T_~8A2;!(+TIt3{Q@lW>s0WIju>f zU5}B>FmYWajACrP2(>ddsfJr1+rC^iyE=N!Q(AmM)evt$s?DHk=kQDMp^Jm7WuMI} zEVT6Yz}u@AkttYDwoyj0t~bV&iwQ$6Z-hu1D*`4{n(7$!%n`IMR~Abno{M%NvBV{Vwe^lrzbtK`9s4 zU87vJdEUh*V9w67{lETm_)VyI<_8Kf&ARtLG`z+zXLOoq>FYINoYOF9k0==TJekb3 zY7OaL;d#Rf)ogf+|EZ}C)dgQW2HvVaKf;dGr7hs*yHy1pMwx}e&QU2X;tgXxJ1A7t zhXigwkWu_VT%rs_D+#_?A5?2W%mo9Q$d+13_jsW*$4)=73Y?zp(!ysFPKh?O zy1PmU+FG9^g$#j)4(o`S|Er-g5-;26##jA-I}=Qt15SEYQ=eeEw^ROUF}y*7T_XMF z)zY;PER<(S=PXYi6^8n6-(CHxF7Rrl0^?>dE zMmog&%3_Tt#Q_yG-uxqEgG7qG((Ca6$?BueSCKv7mkIXhGlvSj%1C+agN$%~g~voK zJ~NXQ(f1m~^)Eq{?@BeVwlXlgjZB9oV7FGv78qFB5liz^HbI{MZ{uc3boO<@+`m<= zJpqSOcMTfkQSHj8ZVG-s+e;vBBg}g}5((xcBoW#VVi82%G)_N#u95@RBb$}Y*&PiN z_6ZVF`f|~5#GtYc>qiyXH8&(*st%2(DX5v2O4o%pI3ed;|Ic)M>)0n(EPKBwz_hK# zgp)G&(aI_CeajI8Ph{da#uMPX(+&kBnOEL?H1|i8@>n}#4e-_&ojb=;*EB_rclvE{ zk)9KhhmN%E{cfdcQ4$eC0N;u^@;jkwJyJ_`-6!<4F!(yy9brq+WG}qSuO~0v^s)M15&qH{L0s#`$rU3xX z4_tO-m@~(q_hoc;^*#OWnj1CnrD)qwtq^wxF-ADOgSgU68w+(MwbQs=^$30+vZtAt zoj3hBcGV82?A^#uc-O2SUhkjvzsrxBD3LrVmvyezAfB?s*|2@#i4wFOPh8|o&js}6 zutCpCy!By|F$G{11Z0m36WFgF1lgN7zc`y}2j%-`XNxz+UGO+m&FWhz%7jLh}q!xmVy ze5@*ri=C2eS>jAE=b}P+NH+R9w-h?9y%_%EdNEN4tE5O~p%v8xy$EDn`9Nu|$v|09#K64`dyBp~IJi_$yT}r?Ws@@VmT2AA zSivr;6;&!y85M7*@15J$AW%WfMFP?r6q_BaSI!9o5L`UR12?bR4%93WF zusXgyp6CSMAovW&oq~29%sbV;^5e=aOe5BIz5)VVn?8uCcUQ``*%9&NyZGIqJ?YBb z5eQMHe%H(f-Fmz?ed_&fi`VtXk-U0tq=rALo4m!-Jc&^dmJLMud4C0ZGd6MLS~ACA zKvwv;drdKqu}8+UgBlcw$T^h~F5A!k`NPotriMyOV*mno6%@RZN1T$}db#ryaS==F z16n^S#HG-*XPvkDcQs)Xs#K_I8gHI8xjEodoez`nM6H`7HpMcR@@e1+m;H(2RZU(~ zRS)E1rPiXw0t^{{RBFCMN@HPw1{3I(ng@8U<8Y|I+SM&K@IIt7NXEgegeb3arLR-s z_pSJ3ZEhSQ3BJ=8t4Pd=q$vtZF&3hW)Pd0i^n!4oJrqn{Yf3g)657wZ?aD=a-)!4AEN@Dpy_ z&~&vhh#0=D$s{3U&n+U^Moo(LhfuYBS9Y>nZa`@l7Ux|D&^b1C+_pysp87R+boe>9 z))@Z^`=;U{Y4Is8)HJSch9Wvpxt$1aaa`Lr=9XLRhFx9^U_GV`3D$qDQW?sc(-$CU ztAs(;<&l(CoMPnN{;iZKsg0;}gYiA43c(@~pz1rX+&4ix{+}&|6OhHsZy6^-5EOFy zVEcXMW0SJ2*yM(Im?W=5C8#NboY-gaBSR3lyGgyH_?a|t6*tk9q!FCuAX1wgay^Lf zWP!eIW7M)p{b6(V#vdb%k#Nr};3wqK0 z+D*Rn{2F=1lU`p(3GtWW_Uax^v;HejtFTZgKbyfAxB{9RoZBWrKQPcuz|UJ|hl?{* zFDx|-T1diVR%!&_N5G%oD)7JkESdZ?y=WBG^bGVxa-sCGYYo3aU^sxC#_W|*bK7am ze&$~3SC;E6MSCpqYQZJe$uI-bJxddUAs8J(&HTQT6+>`{IxZhg2u2}*Tc?V@@cF_n zflxGg?!;-in($i+%lTz#CsX&?CI$1pxHK0H{_q5ZQBnhX-e$YJCCnrOGwip))()jb zl@{6xPnRjyA-$6q(xUjOTmtNz@d3k;h4}H^RN-FF14LQ~C0lH&qUxJwQV8opdE_BP zi$^k+Bj!k^0AxDqj0{I`{yWYBYS(mhdVm6$b<~7fDp@}CE4-xwG0Vt61+a3|ZdR&R z9epN@l-ZH0v>38UAfTU!W70mbIxS-UHAgB7dOn;M^|&hTh6x3eLO`}E{t@i>#@unV z{~^xdmK49mUNe1ayr{4Iba8O}G4Wmt2AB2GJ0bRwir(DasF+L>u8=#u*QqlvWjigN zy9Iu4FVZ(TPffbIA)Qf;DEh5&s{I|w1X}0KU!;+V-5vt~Oa9YvYBG#t-KF#;q6$vS zL@q?=Z!L$87F}A=yVXxb#_`O&uQK4~OxyM4S&7}SU1oGLu38#i*Zg6u`~+Q|s?0rE zMDwc61xfK`4+H;;i#MZaQ=LiX_dfGx!0<`|QafW>Ql%x{QS4>TlmiI(F2U zI#<}tAOMCtJ-r|rt+wIRkf^nmOm?^ZVT-G##eyDj&JIimU2f7}q;Eyz{}k5cIb1u- zkMh8Se}I?SSKd~HjxV(8{-2gNIYl`k3G=Jz*$uoByD4Lr(}P^Vp4qK_6HrtsD&Zn^ zGZ}WVp!%u#ogm#!q^lXg)C{xf9^T09#0&z6nCeE7^-pOfEV}JUlbc$NBHL9%k99)( zF+ET_Am0UW2nqGLY~-Mayp)mxUJNA2SwD5uc_+|8Rk`D~cF>C?0I34qvU&w&GHfCc zja5fMwo!NdYNtSba~8W#_v!urT|J{ftV0z*IZHM(xG&-6Z-e-I0_c%;6_&VnmJAp&9NlrF)i z2(ReC6&1>A&W}HMn%NrPW|(P}Lg!^|jIZTMf`ZM{ys7ky%$!K@0v~+2#uI{%|Im^> z&kwrLJRKeadAq3gQ;x2kLC%3py*DqX2*FJ3a+nce(*Mx~!|!MbB~%rXt4o-Eiw!QO za{~#%Sp|mv`ZgvE!v`{HDX3ZKr0GomVO#QY8FsrPckO%kKq8KX72Pb2RTYK0nwz0< zC6>FvGgz{!OO|2~-Rh9ft{G0iHs|;VZ!jRM|68Z)?@i3dvzDYzv;@=IjRCI^iI*$I z71mmGyf_)Qv=O3Jcjx%6OlFCB4uO55zPH4z0G2D5J>Vcicm-Ow07O_-#;!s{nie*y zo97vxMEqTSu=j7`cX6IK*#{=pi~mf>q3jxIT~OOO%n^MVv~DmQ9!|pt396lNcpbd; z7Gj?oBbQ2iO{|9Ki|E;5Z#f+0K7hqz`MR(Ix(}+qaWId~L}RIT?Lzq&BQ7giOJ`zs znLvLs(Z<{JST3i)DW(D{aLJ<-rb6HK{Q(NtnBDcYQiL*o9G&K>ke6h?om(b+Q(4wg zoyrpbUgzXl;X&Do$s_mDd>Uw zwd!FC|B8v=Zf7-7tlxvNg9|*kD}*xWo>knTYh6L~_d@u+Rg6)(U|?JFS7Sdw{}Nh) z=%{eR<$C2Ai1Kc#od%{MpO2ccQM~6sx1~SfwSt`#AW`LWw1QUuwB(h0SZPTkgL_qu zM)&oFFriMhdJY)+T4z4<5L}YmV-@j|xiGhbz;vlm)-_lQcRs*wn)hE^*owI=acE;D z@ovWeZ30c+uCnDcDtiJ1&W!}$o0owzIku4hfg)D{C~Wwv5`CuO;yiUt28N&dnYuKE z{RtHTFHE5qCIoe-uc<_Zt&0AlYG@Jb0^e+1#IE*x`@XQMnSEz2jf_b-V@1PU_XM>F znDJwWM4NOF`Uu=k5umg+CdL)j_r(3`R5%{m#C$Y$s3e=gj(AvPOB{iCCId9hdKMAhsG_ohbPHi{ z8~m=Ut!@wWGsGG_;(#S%uvmfDhjDN|p==lK(~yXvLJy8JnlI5&ukjCTkKXgIgZV2% zA_JC73KlWd=5?M*tNILqB!A}pHytc(9uIB0Tu>J{|I7z-xB{YJIP5%DzKGW6&R~fmU5^x zi`ka`c^H7JdI7@I8Ed2uZ4g`!k21ek&>tT|y*-}s?MHjo)#v@qBocjMyhjQn_`NEx zFCQEe>!D&Y_1>)^y_n7t?m-qGg<~LG^OpJh0xjQr22RK+ksSq;>4g@=-(TjpMoG`HLv&`?6-WGS^-N8yBJU=I4ejyab+(jMv_ST>u)&L z77zflLfs+fqN3jZet~j1Ov}+e7x%E>E;W}Fu?`HZ04W_?4{d-ES6FUpC)n=S$4a|X zZ%1tK{NYT(kB$e>atX=zMfyuxLl+*WzE8##+xMycj9I^_QRv_o94tF?;MI^mtCii`K#r&AqktG3({oUC_b1h)5Cw zbZ(yZ#0@R_l@`0WYc%ZF`hosh`+P!KAdX0=o3tx{C$TNj*cBIw&^g7cg)u38lL{<; z9;(B;QUi+nj~5`~#GZrmCr#MXb>Qkl(bixU^(o>W4diJc!GiE2>0MJ84H=*JYJWCE z&k^^#2tEA18v{|IO#EVE1CGHjv?7xW*6U)~Z3@>`omdKT-pAr0UvNLNdC&GHdhX6t!@5^is=Bd%vri96) z0dEVbZzw60Pw!)}12&$eMT$M0P!IK0UOBnf+#@0Jf)K=@dDK#Qb{MN}yDGq*Y*p}- z{cQ??0DtQ6#P5Y+*t-pFHPm|1|F`HNY}->zxe>x4Jhs&=O$5J3LSHMx2}66@tjB*n zwGxp4yn~2#gD7h#R&G%hQ{7jlgO{Vp{sX!z>1}?;#*N`j_^3w+hViaN*3-m&k)x%AbhAN!QmL@UI~w`rNL8hlL`tE z?*+p-qWrmF_WLqXk1E=kAdNM`fx2~1ogZRQ5N~I+y0y2djn?`Zr<&!sCGo%Cm>9!X zE!J0%a<%eFmqL-?rd=n41Vf$ZDVmrFc3i*384EyY6b0@d_^PKCt5G7shY7Bq-WoBP z(IRhFg%pJH(w@Zl-%}Fk@BYy1q^0`lFi^BoheKkv(b)17QFw!lMhE8q21?@p8I!ne z?6j)!s;%0OI|(rbUAN`Kz!AK+i*|x1Zl7Z&q?Oy_T#mB?pcH!;GZg?KePxnft>krO4) z{`${+z5$9`eqjrDt1~jX$QYogynznHy{z~{sR#Ed2r6m;(nn_(_QzCtK+|jbSF)0GL5>6mP+Gn z29ToP8?ItLVi=_&^p8`jaqJjLOl7!TFAbwk5yS6FSqlbJpe(K?VOtJD zR+s#EEWrkXcriH}RTM&Qh&j2T8DlV?I5+YGc~Ta*LBE92u33sgaRXP13^c_goy< zaAQ~V8U}+mc$u_P1$VQF=&_c^&)@!|gs2E$vZ($6se4}(3Sm^-PR)Yl7SE{_B(0YL z{|YfIFRK(dpN5qK7rep**X`T$RzqYj_IiJ7;Pk}W$002j0%SQ=x*}%axTaL}mkp;f zWZ({Nv~j6t8|;c&(qkk*TaR7R*sQJ+mDe-^j>JlcZ0x_Ib^bfyE*Lz(YzngT35cLS zpwSDeNI?u{mGya;QeEqNtm5BTf8lRttUgm$2`;RjpcNY-%D;r{^BB=R>t;f*2}D2g zoifMN&Y1Nz|toxT7i20e`6Y;YkVt!6^WlI(H|i+Vp=??w-QcECF+f1+z+os*&f4?nEb85 z6)FBco*WnsL7N_{=-|AI&BdLzE^^ZFSz@xGBLtm}=8OIHS&FJi`z1w!4tQ>vWD*r= zZOWE8@8H#FjpMw<`Bu<$kJ|R3QK&2j{R(-1um+!3fETjSuiKEjxRs&EPj2{MxlcC= zaHbL*eaXI@Y>PXs1hEv zub?5BQ4(25gKQl-xQ9=>U(K(dulC>~AP|Tb^(077(5* z37wV@0M1hXoevluyu-6==XZ@(LFl5fHoUr0M;Z)%pc=bs^)To(GgZJC9RRPzg3SV~ zJd-!j2}AgKT7$7JiArY@>Nkg6Sp=Q)`#<@tYdvsy)Z{~*C|Lv-v={SEw%BPOB2B{6 zxscJf*o_XQW06CSB$vStq|G?^eZ7!d1Nh33J$(}@AlwG$N)Az~!Q%d+dk581c{W0_ zGNtHHE1&A>yTGxofEUuUYralw#rIxLs`A{fUQ0|wW)JB=K+bobd7ofym5(OkGZ8sU zI^n?hC>$lRTPGh?)M3MML-O*eJ3BCWA5iYc#0b^p_ZZT}dK@Y^HS$PM2-F)a1p|Cu z^{tQo#L!M|SHK2Tfdf@!q=$ew&OtM*+XST>OG#GlH#Svox9G2OCa30CKm7wa!ua5l zMfl^8&a=AiSdfG-#AzOtBb9v+}5FBZw^*c3W@3F{uGHDqQ^aSDeGzyLUlXguX2U}@kXD}h8C zPfanQ#H!RiE^!DfS`89e!E6!;U@;cM!bs5U{qXUYi8}->i=e0AKRz$39*~R@(2AhW zf}(UhFv`U)?-!kll`5RqOz^jzvv^#{Cjx!7zUH&GB`~sky~V6epXkBR4@mh0$S;v< z)u{w}rwf9_%H$uq(8Y70HY~n}>UQ<=(7a>8s`XSB6U$Y6D8!F+L+LV9vcFKUVM}8* zNC-=Vip^Z;-XR=Ody*861VCr;8UX8yPS7gL$zshF`R<;&QxGB4ss}i{KgHLDI}#J6 zHH3Km$HLbFxiYYNHC2vJXWR^-=V2R1jkhbJsC9iTV|p6K#q-$G=UXbe77d43!^4`5 zk<#@6-@l+9V!uA|L2LA_Z08uovdF$7H4MVY(9&fUrEN`wQm zPC87@d8?_M!6XW`8`h`Qv%u|UV61 zqsra|<~3s^9iM(h$`xi6F*dYF+RDNs$VTaHxGJ={WF+`nOSvY z@&_}C2YDJlDeC@Tz8cwsP4beqSl7p`w@j?#uf_UPz@6*=W*EFS!9ibGSeK0~tnJfIsm2R3{?r>_CdUOvH%U1* znpjxO$|I9ErSo9gu<8<*1OrpDpjU)l`7Z85Ipu8E9OmJOqxB9LY0mc0|M5*UU@aiC zeJn4vQ6w9c?m4nf^ACt4tRZr?=t{dZ^PdaTxi8)JP8-1rl&^*v4)&CE`EhETl5)=a zfUt0Ey?;PsLK#P2tMhj52)C{}W~vw2vT`g>b@k4~DT$R7UA~n$Yj@#eMq(6x4F;CS zODgb^ZVc(4Z$tg4t|#ofhtUD-5ZliEq<6dD&26!G9l6k>LBE%cou=wHM^Bn}Uo*Z) zu@8{nYmp7T8#bi?Vobb%=7Rji5jaYA@p=B(PNairSjOAN%N*UZ!Cs=%eprbtG zL06m(sjwt_yCcU-LdI7%*)k{J6r7R3^U5wP#4(|4gbyh(;0}%>eAox5jNf|AoBZco z;$j+H$fiwd`UTHFXIMrv%hhx^G5J=7_pxLe)`g)e?hVzD{p!>-$2lSsR5599dP~|J zE^E2eX06zVzrlsno*tnL?t{j8#djsdYEEYIX| zhF&GH?WmR6X<`V}0?DgLD`865F6^XX_W}Ym1g<#@lQjy9k`a#B!TP*;yaeT&%0IZy;9^DOb42 zFP-1{9x<$n?~N8{+I@^~R2a2LH>K-!=6ZH-@%s7R?~n+_7S&nUkGv+j1WBfg#~b_S zoozTjxl{Kmnm#AfH375MEQ*BYlBS<{QfRAidlE4%XBc2uTOX0cY;H#07J-sKdXYHT zEKEGaY$=3Gw*fj&p5Uv!qtd0#hVMxRRiAl*Ae{^4~@%FMfG+=b#DE5~Sx@#-dZrf56f zZV4}MeCIikqNb66`#?+nPQZvI{?7A1l&%T1%h~=p?;o2K^&M-kYl?>-0zDz{eizhN ze$rrWB`#|RW>h8X|5^hNm?AcGn~-{Rm!bTKvFt=C66@t#I6TB>1XC+Hpd ziu%|7ja52ZV~(sCE)6A-)gk>+3;K8o)we-F@^nrdQ3tXlqX{XE66%4TDr6qgf+@-i z;b+Bbaz0KnXufh(r(H;r=d{UBddkm6AT!XbivV1I8m!U3G%?XSVD@2vTs3VTl5Y6X zIc)zAAK1w9c<&YG;c3cjo)U+i?yC}X!3N)bfM7i+33~kBnzAdikfjEOTFw{73QPK> z(}Mq#LyC8w?PHtC;lHAjii5wW5mswUUF?_bEy?kyndzn#iK%bLL+t zgmt|nDHH5{Xc;ymH?C;o%}EMv1~dMf9*)K|8~T0B2=M;Bk@9g~^2{qH*_Dri3#YyK zT{{5OeiH`r32J%i)Ng>Jbn*ZGF__b$RHj3l40DLG!0U*|be^*D+#1D`wFi=9!c+}Q zj2Zc|*xooHF{PB@g+@fn__<|oi7>deQ~g~-*G4!w1tAPEJL0zxoFSMG*CQMu00P^k zB*IxeCT_U;xuAM2 z?u+>i>|U4upVGwmqoZ(e{MczwWjNhu)~)H+0!LO?0vE!Fb4RhQ*N#8%{tUw#R{P+B z$@qavhQzTf@l#m+W8aDael>y0Nv+K3m^rnEulw{N5U2}^mG-r5rq;TlgXTLl?&xMe zJe!;yJdGN27E45VagxONF0 z`tb)aL`FMfP2bZ!plrd5#5hBcmf7(Lt5O1EC5HW-VtpPrVQ|9((I}~EvTIIpMCT?{ zsYO}HDXil=M(10RvirK;7@8B)crXW`Z9mEWSd!8!ArWC1(5{cZeLSg3!sL0k!V|A` zlBN!5@9uFZJo?HyFzpsOTHXTSSI&;9Ez5!v-j4|Aqn%2A^d9hmzsxWymxyJpIMiV4r(h>cbMC4@_F_f7B z4)oXZfzK7h-}eM0bjS1PtaMT*xpgoLdtw!_@?UX!GOkBetd>IVydDSp-`#mbGPS@8 z4SoQyok1*vY)AgqFoI;D@+LR}Cj;gGwPQVd1X`p$#`p%ugrSM!3xrc=Bcy?B+I))wQdbCG1*+z3&BEPJh9!A;wnmm? zB#sslx?}Gw`t|LylZq#Mie?CpO|^9(db1P+IE-0q8ufM4^OBEgE5FgjYGSnGbXqcc3kM~?RAAZZh0(=6Vx-D3xF}&1&Vo?>f$^O!`a4sk|B1rSR zt^a9f#5zY$Ds`fffXevAvYU&|c-Cn?(7YH5j~Kc!HN^v-5nX)U&RLQ1i;OP1wQcPFY6m~O}^-li^KbrL`DXUDOe+p+g^acsG3eyV9-g1@TX7*vLfo> zHCteyxj(FMq9R|#7?9LK8#EBVFYg1K$@V-DJGR7(>XTc52=iJ%fb+&>VN2{(Cg9Qi zJS&Z=`xtQDLW7dJA#&u8o3e&j1 z{TqYhxSHNXC!EGOEt3~gFZObI)FnO}49ejz&sCS2ir(BC0MITQ_I87DY@E{clQs^1 zh`(1(TQqFcqiYf-oulMv-5szn;dR_zK5Q7TAfC@mb@EtRT)&}r4V6(~c-~ByE1ySk z{;l)YxjP7#&8wxxU!Fo;>z5@ZT#J9LZ|*9Y)m^dPNY%;7K!29qS63p@-1)qd3|9Rs5qV9YJB|1mD4#yixH8!i?OUBU@m#P@{xr#U18=rbfCgsbgcnn zjuhj_4Cl~4{qz^cHyNN0WhUxK<}}f(Jb4dy|Ivw+i?VZ8}i<>BttyxzEA!fWVHI0tNY*r zx(Z~2Bl`7;1aH}yGBTYKY^+A&YCTzP`YIR|{mtfFcba- zwCES!5kmHXP4IPEzdNO&vzZCWG=%lp)wb;eTp8`kn!JfAxc+ri!!u@e{U;aWqKe{$ zjK+v1WVjoW zQC)Fcva78A|3Ju8up7?n1q)lD6tdN&$9QgIcDy+y%b>d$788wSaSRn%lePyu0%zk40fm&g{4;-x!{-qvb6 z>yz`mMP&cX6eQmo*d>uv#-Mv4Vwu_Gs%f*38R||?WEs(-`nwhKZ}=>+>x)oSl%NPH zqj&wStKl`+GNgcgY@KL80>bh&SP&ZBoZw#cOB<1bH>3>evwxy6|J=&}pm@^$Ik8Dy zJ|n>2#B(8|WWph`iYr1Dcp%_(nrXVmqVx~-cD4erS;)qpCy&*r@Yd^tM^E3SXO=c> z8~K+zK#bL=w!`#5YIIWdk3iIe^DkASs|gYp#RwOw4C4-uBn~3Uxr^_cA;!;qxPqU^ zgPw}#xb4O>8&&udrs`f+JbRnRCtn3#G=UJY;3V3MMck3c0_$Cy=cNTm!;w6R6a(X> zrhkb}=4=a2(m5<;BW$6;Ke7V1ls*@i`+e1T8gB|APWd@d=V(~ct>=D+X%RH(%Qj~1 z0;FkWhmdTxh$g6w2XPA_Bu35p1$!Q=@X-E=Ab(0#5ljfqq5T2@(RyzcvH9RjCsHI2 z8#6(KeHg%X%h(X1FVmbynxS;a&MbKO{9?8?rbQC5+c^p$TXY+J|0=KdViJNJaj?A_`N+qn4$nt zvBU#n@#n`V5UfdpqfGf_+h*cugpXeT&v~b6iZ25KGRDTqNL}@zgfdi6q%;cxF~bPC z($exVC*FOmHA53D%)Dv$#A##Ueb0eGP6E8UB`%xIh23A0NLGt548+R^MHLC@oBY{# zENxQhl#E_ezg1mvKwQ zA}fD~zfOSm?aiqS2b9_Fwf=ZQKPH{J1DEM?^_A4$o{5*LSIcix`_QMW{kZObkT6J$k_ zWtI)Iq{;EreSPON1Er>KX}b#W^&w2GhHe4isQeSu^0wymhaTNns>~PEk|xG*Ip^Hl zfpZMXHWzf#`j?`SF@*&iz?*c2QKVP9v~m^^NK^mhVSYKM{Tm0sO$-NdE?=2=Mdt8x zQYca_B4v^`!4wu4(O%_laJc}|DVV_QlDAbSsdp~0C#cO0L#oP3CE09fC4mw97aKlu zH_%4~-g5&b09?O~*cUML!2FHEypk=%hqMegi+l{{%yUY?z(s18`k0`fGTy^T-`aq$ z8~6#lwck;eF=R<(7+ut&^s<<0{89bPPSR7{}073RA&nZOsj z_d?Q)J)#fjrmTk4V{7;4RYjLX7f3uTf-hld7G99EYMuQKKCCo#6p5WLVeT0}9(W3_ zKwji7tOqkF;R=XI1gu#*LXUpZLAP0sckxER>-5%d)RRPEUO>=PFLoBKXv)v>ylq|X zvy5Z6AqexZGDNQC3I>j!{zT7}&)GHI+JI8smE|!wx+>P|C65j*;XGVYbyusmJk$~j z{)7STC$?Q6uBkB9O?*EJG&-_caD9MlA2(bnM42-Bk^{t>RT8- zPcBYNYTS6;CR6qj9)8O*T^dhVlqA$a+uZyxVLLGNbpry&{_~;X~n#;Y3m$Wvxeu*>pEImLC|X&?>r>Ij@(5@&l>nzriB!NWXxtmt<18U9D%KWXTx#18*>yK1 z*pWe8e+5GfBXoppk>`>UOF4%?t+iDKtdehd&tRuSV(rUc3mD z>=`IM(Yn{0sRgUUZlYEZtaYglN4*JG?BX&hDTDx{L1&JF+KKfZp5EW#(F9&bk&Q+4 zS+8dZ;POb*&J*g>f>G;*Aj;QT9=H8}R>c4l+DbB@vQQ$fUTtk-1e1^Q{J1vf?K1qS z@x2)P5(N|p{A7L%sAQTCW}bgAP0IkgmAOk?5#abdsxf|vpaKj=&mLo&HlWWNs?}qU zY`i@75x4!Hy38C27LeP*V2Bx2i;oBzda2(fn?R|x3F!>Yze_k0q5~&c>8VxB0c$La zpnm(}s^_+eJ6{lrzQO`TdYy)u#0_i+u7$UCBSGD9dAe^=rNRk1TGQz~_>B%%Nq!5Q z9-V$8DK)`=(c; z`PJ$*h@8HGm+g#A5#iRl{=J-o8i~Q#23t6?uG|1yECoRJet~owcahLZ+9)8MM8gx2 z+dmwVzn!te>!DT13@b5tL5`?KRsz-BLc0g?ER&%~u^$s){W(LJhPf$F8`wB%N%Z^F zrfh9Qp3&3UazO6-<67;t3fRhv6XyNVN-2Mb zpcUE8ebBU4Lblx@aqzNhn->qN!t-2j(O=duz9Eo^?@HzvZM{1GQu=)qxS;J)igf;)7#HPN+qcY_{cVAIK)#*6i;Z4}wEZ!(~fB`;ZU-(XT zKi?Shvdk}q)f+fScww2M`1O^-SreawS}-+}8AaE+RP8$>Iu5D}tt9u6Yy+(;3oZfS zu6(eR!hcDurIB^C8ai+clE&bEj_XdKK2ETqCnilxwHwjZO7O#zp);HhI9LaBa!fD8 ztXR#)l*?076JDsqRj*%VEx9Np+d%78X+A@C|9`)##Z_zCFDN@n>vafTHSX*nm4Ban zY*HYmY}gU1;m7b!s25i&GwM_B8-3Ogx1yg?JfTpQ=foOLEwBMN-O4ynGGPDC8jt!Y zS;9fe#~Ryd?p_s#2tAM^O|SZs+#P>7rr-j3zfHeM9J@& z2`jaf9npkwTlDVoFo{Ok3G4^NX~oF_8R&ME7KX%b#BnI|skB*nI3V1C@l=|kN4|1t z(Pyq=<4JTES@?8TQKyblOT`toR06*#GK2_u4bx#dJXKkv1r zNy6P(KtqzPtqm1a!%d;WcrqojV4us-y5!qG)A}nMy4;PL0c6b=iQTrD85wOtc~4{x zssF50;HMGP75H^Di@F488}8WGi?uKm+F4De1M5pQJReoCvVC#>V3wVwzLYSaucbir zt;!BcN3&iE0c};2A5+z>&eD^j7F1G@p4PJm%_aOQ4O!E|dQTnWT>wnf{o|$wlA{)| z-s+{*1i1f!tp}B6+COyxeKsZ@M@uCe8KEQ9vs}hRI6xP9$pZ zAW(<&y*}i{ujT*-Iouh0*S#3#tj+~#-X#>cA-s&jj*lmA@4pB3D@bSQ8L0~$?3UYn z8t3bI7!-3Mh{6ihfTcKGItWrnnE9KbEFr6@N_s_V1#w>;ODH&R29Z}cOO$lRVrFUH z%tH(f<~9Y#l*DLt6RUrd*f|d-Z~NPlN2uk<#pv(a#EY4b4>0d?L+*0}7g+vZNjy58 z&QA`4w8Xr-bGmclZVy z;^5GHwG{P=QqUg+-?qSqGaH5$pdur~tPaeRS1KaH?n%HeR; z=5=3ed&H!et6yxUi~lDdHf(W{f%QRR<_sm!rMVXlZ=eFcXv(geW`-l-dR7T117S)VrK1hCjgW+D?*=sJKvwHzU95;tcn`lpLY}enLT71}!-%^rahLMc) z_hvJ>a@>Rc&bzbB=mQNvflW5UAxHuz&US8i;%Hi?hdrcI^bv8kOxNuzRXsr6p}BBd zjf{UmPLi5h5pS(Wq$nB5s%IBQ^K(oJmOj?Yo)$r~91H&z9VyOQh#L-uq%vjoh> zMt;Asme0jprAn|Z*P9F?TZz9##AKf&`ivcXRn2ZuwB+}h%Avi2*0OD9H}c`0E%lqW z=iW)_%|#FwU~&|kD-IsGP&`&im^x4SO;`LbrlMfhozd(9c@nXw^U<}Iho4C;J&ud2 z=sCk21tbRKLwWF7S=qrR`{jbAiT(%x_Gwo;^CvjZvLQwjxT1WY9`BX9q;dNQ#;7Sy z^RXD2IoUp2W#RI_8c@KaFA=U8l0vnGn-_6T?E=RxSO~FevnT|qKo$DzXLpFZ=Z6Nd zhHb~t+?3wM?@d_0wVb@*fOWc49L`bKTST!H1+}jWa1{5k4 z2KtEuRAfky)bp{w1(lMgrc}S-&Om|l>vSa5YMkE4ghaME&B{)q5|RfRE&YI?ge@)1 zrdI#nM&RQr3I55Bj`N0%`2Y2JI8zrb#wg#%pR~mi4_6gVv(61GU)XxqF~6r7=av)B zQSn3mMMU#T6wEGRtc_F2($F8elq+XSAqgKXDNC@<+cgH6BK>H&B?#mo_)sm|KClO( zb16l>lp$X!U+Fo@g|m_MfVltTpVN6c9V<#iW0eQB8@cIC{FnCt8(ay15o!Q@Hk6j< z(mqGBl*SJSNEajdN%1fP^^jycydr%5No@?^7Qc9Tg4jN-MPfePks_E>YSK1S7@p3| z+LO7xV7XSqAO{h5g>|hp5sL2z@^wXYs#?Y0wCJXdQg7tFW8N1-8Oj=KX%EE zT~RdcF||Y?rE?Neicl!)AQ!%x`M01Zq3>ZafI! zk^Lll(iqd*b16{eH?_|3>a-eBtqU}!7Q45d#JrS=c`Wr}nLlDLHgkjHwss5Ws0!&m zIe_8+q7gm3X~uY==Uk~V=!=6``}EMtpn=$5KFcRYr5spA$W^%H)0G8J9lON{L0V7x z47LM^Xs}h-UR9HGuGo#2+hs3|KCj@wLlj(8y8hG@m`mbtT9OqP?8-MiRUBK~hHtca zGjBb(mX}BBMz5rzo~I9PAq{2?d!YY4uM#zB0wqv1lPCi}1n^;x0^MY5_<%!i#(ITe zc=0K3VLRJUb`eCk9?p&GD-wKvviAOIRXPNkx7`z`kkcy5L)Z8A-!mQIKrx*XBoYq_ z!VUgVb%JlmnS>+5kl6EcT#pFyZth1(<2(iyIcdS0(kH@Uj*O-lT+a|Y=WV_fq(V($ z;%F;rQW7?Ewuu=NEvh(pu1OEgM$Q8)cf`iGdCq^6GO2x(NYBw2ic8TW=rQ4+kV%*- ze7Qk$%5!-ww%JlV5O>EcaHtZP`Fv@Kyl%H1I~R;+>ER$^ZBJUmk)fhc>KsCXTXRJa zp{9k%Dm74M{zf*&^obH8o~M?sGF$ZrKv7nA?*ttd>@J5vq$dpL?Jj!DA1`yp%ocGp zmBusSddS94sLvFQw3Fva`fT~cIZo3SbnKDU| zk_pz?IGO_Vwt5`zT}9^CTd$lDGqA5?H&q5?8e#R~Gwyr(Wpai@L1hLWESCB<6hs^X zJYNiru#R#Xy4)w4}D;#KUKror;#gf;AsO1d1=hvWFN(6`& zFhArx4ypEUr))$Ry%IF~K^DpZi(XpxciV#ISy$oCQE7=ZB&QmXp#Z~yDra0K?~o% z8bYAGC0U5hRHR{c-73xkHwPZrjTC zqlQVTPOjAOU-348lRLF4NOK9j0>D+W;;;yi4An~}$d}OwL4E%>(>U)a!acRq9mo5^ z-jif{d=(*3Td~f`;i3hPaVgU|Dt-u3ufDih>ZgthX{A|iV`QY@gxiUfbl!mYbGEt( zSf842MQmL$J+iu2vK03AqfZAbz^jGl`d7v~;Dq4^WkqiSkXkNm)2}kV^)D|++Ko35 z==tNuPE0={t5UP&h|2+z-WpXZOwz56W_01NAl)5GO451h$goHJHeBBI3Pi|x$&qQw zW`>dfwWKJXM#yqQSl$8A0`bTbAVH3cguRLnJql=|A**iX2Y`ZKLk|DbriDY)|fD9H1BvKkO3g2j&_p<|JB4jB>EL%y$ ztF|r4C8-S<5Tx2D(Irv0lA5>vBkVtD`j>8N91#?$(PWE1#ZwO0q)dIC%^VFLjN;b& zGR7G_`bJU&l@vh>EJ2u;A`J7#biJydf!A95&&dNQIHD7gJ;2No%x$YbQ@4nA=r)wB z9kcmEMIO=1Y32{Hsjv1eRoVqQgh9k;%^s z)Bh1(?pR>XNbZFjqi^<8<+*4kB9{&8f#^MVSr|HGoj!CI3c{Af^4xu|-*~T~KIYf= zza8Ur94VOl!XaqEm$oI_Wd#xBlxMAG&zo2xebedj3XPEdu~b38j`#pfM-cOLf7Hl4 zkTJVQWu6GL?e+wdA!rP)p`#n%*yu&?zlfg;-9I0Qs-j`^@moYn4-OPVrc2Y?3DgYP zA;}BOw*_w=sH+kv*h&KVOUxRhk^Xu=CobEqi^4aUK<9lwQeaH{t>P7Kp6vjfE{$=- zS%^4IP`3!tSU=)`N}T+SqRFU}e*u_W_STF_rXj3Nk{-uZ{KIid_Bzg8I@gSPe`X>3 zBB>CkXR*vxy=f04m0Qfpwwkgf8u^T#plm!$-k5Aya-ceFxzQAb9-^H;@Y6x$cB)`osLjE3xl^V#PG?KWJ|kMq6-Pq-3UGIRG%Bf z-C-x2tA%-TU4MPoPKiOuRaa0373nk{ZffcqYlZUivZSbqCYl3Ay5`gW#+YHU2pj;5 z-l*ok8VR>~g4RwU&oqk<+^r~lZ1ps{Lounm3CuZc))@-1U6pppD*}+{G|sfHnGkej zXU%^ISb&9cRR;eLP;M0}G(ajjwTq2wc=yAQo7)d_RPQ6|Ii_6Js}H4zy}p1l{`#7h z+h$T_R+bpkgFH+5aA0tBYE#}NA6t^z%1{YM$PcmAu(;p?xbb4BNVNL)Znz#^$%Bf* zK&7xrDx?hv2N(%m1qnlrJD%SWe%aP%j>Ch#$4yAbBrPW+S2W zXUOk$zAJ5qbfJT?r91=|xaOv)r_Q>{JN~5ykai+je;|BKV-Hakkx7Q$yV1Yu{Ahmp z0c{nq&P(#Jl-&(N-SFc-*35L&I-c=ih)my^5b`z6Tt9T)I>>@?d^|q5j(H7@s_(od z{Yjv&HJA=8_iUOoCkG=T0l4BdF%FZ%%@{zmRkqi08^afU-T;Q}3Jmy^j$8NTc?9Wx z;B*1A4A)Nebv)P0jm%E84#Xn)Xi_VYLr+n~5txMHTa689#@gOK5lM}e?;~kA@eU5i zX6GqoaRVZtj>fd<1oxsdQ;{35pow|BVq9Ii6TfXh8!#3PW7+cOAQpGRg^vyWcww6+ z3|4|U=mz@Y;-zsDpef(iHyY*`GVUH?=5XYObF zayZ=B=Y>|~9y6r9$|Q~h_9)k(X?=4v=5Dd^{GqH`8%Hx^ z-<^?wYnp`~Vbyox5I(NU@-pnd*l@nj50USe{z=i%gC7cEWS~}aO)ANJ`|uHgT+5(_ zpx-R3O!h*wb7erw3E1!UM`ZNwuWN@jAIp7iB<>U9G;tM2It2*yj~iU%a5J1w@53gNO`bK2{b?7O{4_gl{l}f^(KC6wg0vff(dsz{qmL zBOswYRCN95P~@6$h>=c0wzbO+G;|8Y4QCfDNKd#+X8rguG>BRL^*s$f+!zFCY3?{2 zDTlKc-=FH&v+AdebSAj-jzs_&YI)H4J2xBGc?BG=(gTlhVXtVKhW&XX(8PoD46 z%8yQzRY=Efe8Ua(ULvwXJ&)G`X_bjlx-u<7Lh+|K>Qt8=i!6@L2g?m9YAXNO88%gz zWcZ(g90CE`{>Mt1$mLZ!i)BR-u|AZS7QpVsv_gzwTe)!uCdtjT2f|C(BL-oizjlbn zV{8t}K0W|yM%6z`5V*T!_V-^Sua_o=@hv1iJ(R_{7{|bzXeK2ZsE0Qe4v=;4A|#lXwu?}u;v!qq`C$s>_Ou5M#dg z4rG1RJ*lWS=q>p}^Ojhaz<{v&bP2ju()wCqyS+zR3j*217smsTXJncBn6u7vF^~Lj zbxYAE6j0*j$cdJb07Mr-OcHvobfkeZegUCfyFEjCI<*F8z5}M|c2!?)MKBaaiU`*P z-m|;D7{`Clh+x4hpfUeqJFT_*9`4H{*?DsI7T*Akq_1MRzr_Zc2)c(2!u<4uUMk?? zsYn+}qQD{Shsw;8k93+j)E-;Z(~l~S1H=dB6QpBj8k?Zlpv%ctnc-5MIDf-+o<;pO z`BQML7hL|z$a9A%li>)}$bce9q_P;Z(6Tj@_&iIV9-?OTwgye8Op(+jMCL7=ur>m0j1oPJo}DsMG!`}kbAlzR$a3dw1 zF8wtLE8*TbAGgNWCP;32jgZuOSfnW}HqPH{+hu!UEAo%>QtSq+8NxwAwCqk+-LjBG zA0S^^P>A7GuFuSDD5uOCbLXuWEvb~I0XIMiu9Fz--Ub;2P>`|-pFqKr?AduLFa04K z5*mG<5_bK_htDTnPG^lQ(>45I@N#3%nw5htZOjPjU0(j0H@O09xO29hBoE6H{t=5v zG1l79XX9)1_XiNUf!nrK=Vl>$I~^I18`KjN@lTZwhHCTftlGndT`(|@yHMCwJ5EMl z9si+*YUTfW*%>4YsN{D|CF^;YZ3hHM>}5uEcWW_SAg*h|+MC_&%aB(v)!8RfU~a>a z*C4Yt>?I8mot1s1CH>4NADjU#j75mF+OfC0#f9If@P}!^gp#&a{t+yE-8 zT1^>^PG-=>q2=yry*Z+wV-7}WHwt8RKbD@}n|D&dQLanz{X6pt*y`fi{F?1;;imwM z#{0D6-X%vVOUNZOkuZjMCJNjsQ9FbFXSn+J+Q8QRY(#Knjy$T|?XV)QZm98W zWm$#(55lj`k)M%E=q`uu=Gq|EE<7;*=iFpVXa_x#J@Vn@edPz!`{V})02f#5d?0Fm z2RLdPG~bt8kXmz(xM7;P7ugxM=02DJBoFPTTFlYt{Jj;qKZqOvHl~$^{GrtK6-|&Y zycLuz+*7cQMUX#As2I(%X)-dEHH60yR+Rx|99RRQflvI@hHLgvP+GYp5SS%0+OJL} z5za3qj1nb(5;Yysv7;A8A37y*iMlGxGJ^^azj*J^=!^kK67#%+7S~-?9b%|KG9-7Z z3xDR9IwoVV`lve5JRMDkrEHADKR*rC<72^&6nvZ~KU+cnFz8C+7j^-_H~PX7#rlYMFno7hcMHPR ztb##3PDXB7Ck|W#UuF|h%XRZj)K1@Z`4fuvX)+HCXQ?Y}h;EGeh4##1F`4UA95v6c zN6h9J3o+>CttSU}^y#x}7*pfLKSfc%qt8i4d`Em89~CG?v>G<>!JDu6oO7``$@oS& zlOz+1khT@RNY&Fb5l+0+LX)z&H;={x`V^FsvtQp zO#BlK`_(<-*GPI6$nKL(5*nM4JW;BTRvJrF?Vl|s3-!5Z->SsQ15x(&?u+s@<)Z_q z#?x9kKxE{C`TC;+(RT%yH0_!tMEI}&*2Qq;p61!&p@z(RZdcsS#c{dh?8D=0a zifaw?gFCVM(TAk6X^wd#H;GzANJ1SW#>7V~e^Q1^zTrImt55s{PXrkp!jB1_eu)i` zZag~Q)NDfG%upq2txca-?UT%`tPlh!mf9Xu6{UriL+ahTBT6zZ) z%<-m$0-;QD)$gmC3=vfAQku?1`rH>GQ-27fF_qSBb%CJS|MzP}86S3>%6FDXsg`uK z(RNt+h3y-c5D82-aEoNJlBfxj{{NyY4A%~dRSGI3-a6=UQhN)(V&^4NgUfnvKS?5} zbO#IrD=J{co0a{|w>1Ln;A9=r?2d=7PYOoVs$>Y80$k}R$hbJI{P@8jf4~X02YX&n z*>L^YVF>LN$R!4a$LxBC)g0Nssv0>3plqa!b!M! zt#5N>=;9EQvSp?6^V~e4_@4y)2jrmG67JCb>luK%`-C`$dkMEOv}+$P3;t~rUUt#Y z%RL#4EjHdaH)RZj6zXa&j6k?aIIn|kA?v)A*5~9o@=Xa~+M>=gMYJ=8Jb8Q(*rd39S zYeSyf{GF&w94#vI(Cz5Rj>QkoE*w92E|Ve!m?nepfGSPSF*XppvfB|1@VocWWJU)s pGPZ@61hS8xgix&1=Vr@TcWJ|~F6YfZp;(%l<}bhX1O1gcc-GL3qFev~ diff --git a/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat deleted file mode 100644 index a40bbe251d90e576060adb7eaa2456197878804c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96000 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 diff --git a/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 92e4bc528e8937a311b502e4940e5e363a1f7c57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192000 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�uc*gWwz@d=S2~bA~LrFNXzUL(>hGG#V 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%0DJH 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�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 zy6-(expected: &[u8]) -where - G::Affine: UncompressedEncoding, -{ - let mut e = G::identity(); - let encoded_len = ::Uncompressed::default() - .as_ref() - .len(); - - let mut v = vec![]; - { - let mut expected = expected; - for _ in 0..1000 { - let e_affine = e.to_affine(); - let encoded = e_affine.to_uncompressed(); - v.extend_from_slice(encoded.as_ref()); - - let mut decoded = ::Uncompressed::default(); - decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); - expected = &expected[encoded_len..]; - let decoded = G::Affine::from_uncompressed(&decoded).unwrap(); - assert_eq!(e_affine, decoded); - - e.add_assign(&G::generator()); - } - } - - assert_eq!(&v[..], expected); -} - -fn compressed_test_vectors(expected: &[u8]) { - let mut e = G::identity(); - let encoded_len = ::Repr::default().as_ref().len(); - - let mut v = vec![]; - { - let mut expected = expected; - for _ in 0..1000 { - let e_affine = e.to_affine(); - let encoded = e_affine.to_bytes(); - v.extend_from_slice(encoded.as_ref()); - - let mut decoded = ::Repr::default(); - decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); - expected = &expected[encoded_len..]; - let decoded = G::Affine::from_bytes(&decoded).unwrap(); - assert_eq!(e_affine, decoded); - - e.add_assign(&G::generator()); - } - } - - assert_eq!(&v[..], expected); -} - -#[test] -fn test_g1_uncompressed_valid_vectors() { - uncompressed_test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g1_compressed_valid_vectors() { - compressed_test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g2_uncompressed_valid_vectors() { - uncompressed_test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g2_compressed_valid_vectors() { - compressed_test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g1_uncompressed_invalid_vectors() { - { - let z = G1Affine::identity().to_uncompressed(); - - { - let mut z = z; - z.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected an uncompressed point"); - } - } - - { - let mut z = z; - z.as_mut()[0] |= 0b0010_0000; - if G1Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); - } - } - - for i in 0..G1Uncompressed::size() { - let mut z = z; - z.as_mut()[i] |= 0b0000_0001; - if G1Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); - } - } - } - - let o = G1Affine::generator().to_uncompressed(); - - { - let mut o = o; - o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_uncompressed(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected an uncompressed point"); - } - } - - let m = Fq::char(); - - { - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - - if G1Affine::from_uncompressed(&o).is_none().into() { - // x coordinate - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - o.as_mut()[48..].copy_from_slice(m.as_ref()); - - if G1Affine::from_uncompressed(&o).is_none().into() { - // y coordinate - } else { - panic!("should have rejected the point") - } - } - - { - let m = Fq::zero().to_repr(); - - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - - if G1Affine::from_uncompressed(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because it isn't on the curve") - } - } - - { - let mut o = o; - let mut x = Fq::one(); - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? - - let y = x3b.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - - // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.to_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(y.to_repr().as_ref()); - - if G1Affine::from_uncompressed(&o).is_none().into() { - break; - } else { - panic!( - "should have rejected the point because it isn't in the correct subgroup" - ) - } - } else { - x.add_assign(&Fq::one()); - } - } - } -} - -#[test] -fn test_g2_uncompressed_invalid_vectors() { - { - let z = G2Affine::identity().to_uncompressed(); - - { - let mut z = z; - z.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected an uncompressed point"); - } - } - - { - let mut z = z; - z.as_mut()[0] |= 0b0010_0000; - if G2Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); - } - } - - for i in 0..G2Uncompressed::size() { - let mut z = z; - z.as_mut()[i] |= 0b0000_0001; - if G2Affine::from_uncompressed(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); - } - } - } - - let o = G2Affine::generator().to_uncompressed(); - - { - let mut o = o; - o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_uncompressed(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected an uncompressed point"); - } - } - - let m = Fq::char(); - - { - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - // x coordinate (c1) - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - o.as_mut()[48..96].copy_from_slice(m.as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - // x coordinate (c0) - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - o.as_mut()[96..144].copy_from_slice(m.as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - // y coordinate (c1) - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - o.as_mut()[144..].copy_from_slice(m.as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - // y coordinate (c0) - } else { - panic!("should have rejected the point") - } - } - - { - let m = Fq::zero().to_repr(); - - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - o.as_mut()[48..96].copy_from_slice(m.as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because it isn't on the curve") - } - } - - { - let mut o = o; - let mut x = Fq2::one(); - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq2 { - c0: Fq::from(4), - c1: Fq::from(4), - }); // TODO: perhaps expose coeff_b through API? - - let y = x3b.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - - // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); - o.as_mut()[48..96].copy_from_slice(x.c0.to_repr().as_ref()); - o.as_mut()[96..144].copy_from_slice(y.c1.to_repr().as_ref()); - o.as_mut()[144..].copy_from_slice(y.c0.to_repr().as_ref()); - - if G2Affine::from_uncompressed(&o).is_none().into() { - break; - } else { - panic!( - "should have rejected the point because it isn't in the correct subgroup" - ) - } - } else { - x.add_assign(&Fq2::one()); - } - } - } -} - -#[test] -fn test_g1_compressed_invalid_vectors() { - { - let z = G1Affine::identity().to_bytes(); - - { - let mut z = z; - z.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected a compressed point"); - } - } - - { - let mut z = z; - z.as_mut()[0] |= 0b0010_0000; - if G1Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); - } - } - - for i in 0..G1Compressed::size() { - let mut z = z; - z.as_mut()[i] |= 0b0000_0001; - if G1Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); - } - } - } - - let o = G1Affine::generator().to_bytes(); - - { - let mut o = o; - o.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_bytes(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected a compressed point"); - } - } - - let m = Fq::char(); - - { - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G1Affine::from_bytes(&o).is_none().into() { - // x coordinate - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - let mut x = Fq::one(); - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - x.add_assign(&Fq::one()); - } else { - o.as_mut().copy_from_slice(x.to_repr().as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G1Affine::from_bytes(&o).is_none().into() { - break; - } else { - panic!("should have rejected the point because it isn't on the curve") - } - } - } - } - - { - let mut o = o; - let mut x = Fq::one(); - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut().copy_from_slice(x.to_repr().as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G1Affine::from_bytes(&o).is_none().into() { - break; - } else { - panic!( - "should have rejected the point because it isn't in the correct subgroup" - ) - } - } else { - x.add_assign(&Fq::one()); - } - } - } -} - -#[test] -fn test_g2_compressed_invalid_vectors() { - { - let z = G2Affine::identity().to_bytes(); - - { - let mut z = z; - z.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected a compressed point"); - } - } - - { - let mut z = z; - z.as_mut()[0] |= 0b0010_0000; - if G2Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); - } - } - - for i in 0..G2Compressed::size() { - let mut z = z; - z.as_mut()[i] |= 0b0000_0001; - if G2Affine::from_bytes(&z).is_none().into() { - // :) - } else { - panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); - } - } - } - - let o = G2Affine::generator().to_bytes(); - - { - let mut o = o; - o.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_bytes(&o).is_none().into() { - // :) - } else { - panic!("should have rejected the point because we expected a compressed point"); - } - } - - let m = Fq::char(); - - { - let mut o = o; - o.as_mut()[..48].copy_from_slice(m.as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G2Affine::from_bytes(&o).is_none().into() { - // x coordinate (c1) - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - o.as_mut()[48..96].copy_from_slice(m.as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G2Affine::from_bytes(&o).is_none().into() { - // x coordinate (c0) - } else { - panic!("should have rejected the point") - } - } - - { - let mut o = o; - let mut x = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq2 { - c0: Fq::from(4), - c1: Fq::from(4), - }); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - x.add_assign(&Fq2::one()); - } else { - o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G2Affine::from_bytes(&o).is_none().into() { - break; - } else { - panic!("should have rejected the point because it isn't on the curve") - } - } - } - } - - { - let mut o = o; - let mut x = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - loop { - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq2 { - c0: Fq::from(4), - c1: Fq::from(4), - }); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - // We know this is on the curve, but it's likely not going to be in the correct subgroup. - o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); - o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); - o.as_mut()[0] |= 0b1000_0000; - - if G2Affine::from_bytes(&o).is_none().into() { - break; - } else { - panic!( - "should have rejected the point because it isn't in the correct subgroup" - ) - } - } else { - x.add_assign(&Fq2::one()); - } - } - } -} diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 5f0e3ca101..c32d9d4850 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -18,8 +18,6 @@ #[cfg(test)] pub mod tests; -pub mod bls12_381; - use core::ops::Mul; use ff::PrimeField; use group::{ From ed3489a11196053977ef5cd9b8c80139b38e67b9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 21 Aug 2020 18:22:29 +0100 Subject: [PATCH 168/210] zcash_primitives: Explicitly type zero-initialised arrays --- zcash_primitives/src/keys.rs | 12 ++--- zcash_primitives/src/redjubjub.rs | 4 +- zcash_primitives/src/sapling.rs | 2 +- .../src/transaction/components.rs | 46 +++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index 2e2d61eeca..fdde7f3acf 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -70,17 +70,17 @@ impl ExpandedSpendingKey { } pub fn read(mut reader: R) -> io::Result { - let mut ask_repr = [0; 32]; + let mut ask_repr = [0u8; 32]; reader.read_exact(ask_repr.as_mut())?; let ask = jubjub::Fr::from_repr(ask_repr) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?; - let mut nsk_repr = [0; 32]; + let mut nsk_repr = [0u8; 32]; reader.read_exact(nsk_repr.as_mut())?; let nsk = jubjub::Fr::from_repr(nsk_repr) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; - let mut ovk = [0; 32]; + let mut ovk = [0u8; 32]; reader.read_exact(&mut ovk)?; Ok(ExpandedSpendingKey { @@ -131,12 +131,12 @@ impl FullViewingKey { pub fn read(mut reader: R) -> io::Result { let ak = { - let mut buf = [0; 32]; + let mut buf = [0u8; 32]; reader.read_exact(&mut buf)?; jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity())) }; let nk = { - let mut buf = [0; 32]; + let mut buf = [0u8; 32]; reader.read_exact(&mut buf)?; jubjub::SubgroupPoint::from_bytes(&buf) }; @@ -155,7 +155,7 @@ impl FullViewingKey { let ak = ak.unwrap(); let nk = nk.unwrap(); - let mut ovk = [0; 32]; + let mut ovk = [0u8; 32]; reader.read_exact(&mut ovk)?; Ok(FullViewingKey { diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index 571f7b9b32..2cb6c45224 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -13,7 +13,7 @@ use std::ops::{AddAssign, MulAssign, Neg}; use crate::util::hash_to_scalar; fn read_scalar(mut reader: R) -> io::Result { - let mut s_repr = [0; 32]; + let mut s_repr = [0u8; 32]; reader.read_exact(s_repr.as_mut())?; jubjub::Fr::from_repr(s_repr) @@ -105,7 +105,7 @@ impl PublicKey { } pub fn read(mut reader: R) -> io::Result { - let mut bytes = [0; 32]; + let mut bytes = [0u8; 32]; reader.read_exact(&mut bytes)?; let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey); if p.is_some().into() { diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 7b007edfc0..eafb087361 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -64,7 +64,7 @@ impl Node { impl Hashable for Node { fn read(mut reader: R) -> io::Result { - let mut repr = [0; 32]; + let mut repr = [0u8; 32]; reader.read_exact(&mut repr)?; Ok(Node::new(repr)) } diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 70e115a4fd..60a7dc8949 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -31,7 +31,7 @@ impl OutPoint { } pub fn read(mut reader: R) -> io::Result { - let mut hash = [0; 32]; + let mut hash = [0u8; 32]; reader.read_exact(&mut hash)?; let n = reader.read_u32::()?; Ok(OutPoint { hash, n }) @@ -96,7 +96,7 @@ pub struct TxOut { impl TxOut { pub fn read(mut reader: &mut R) -> io::Result { let value = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_nonnegative_i64_le_bytes(tmp) } @@ -141,7 +141,7 @@ impl SpendDescription { // - "Not small order" is enforced in SaplingVerificationContext::check_spend() // (located in zcash_proofs::sapling::verifier). let cv = { - let mut bytes = [0; 32]; + let mut bytes = [0u8; 32]; reader.read_exact(&mut bytes)?; let cv = jubjub::ExtendedPoint::from_bytes(&bytes); if cv.is_none().into() { @@ -152,13 +152,13 @@ impl SpendDescription { // Consensus rule (§7.3): Canonical encoding is enforced here let anchor = { - let mut f = [0; 32]; + let mut f = [0u8; 32]; reader.read_exact(&mut f)?; bls12_381::Scalar::from_repr(f) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))? }; - let mut nullifier = [0; 32]; + let mut nullifier = [0u8; 32]; reader.read_exact(&mut nullifier)?; // Consensus rules (§4.4): @@ -170,7 +170,7 @@ impl SpendDescription { // - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend() // due to the need to parse this into a bellman::groth16::Proof. // - Proof validity is enforced in SaplingVerificationContext::check_spend() - let mut zkproof = [0; GROTH_PROOF_SIZE]; + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut zkproof)?; // Consensus rules (§4.4): @@ -230,7 +230,7 @@ impl OutputDescription { // - "Not small order" is enforced in SaplingVerificationContext::check_output() // (located in zcash_proofs::sapling::verifier). let cv = { - let mut bytes = [0; 32]; + let mut bytes = [0u8; 32]; reader.read_exact(&mut bytes)?; let cv = jubjub::ExtendedPoint::from_bytes(&bytes); if cv.is_none().into() { @@ -241,7 +241,7 @@ impl OutputDescription { // Consensus rule (§7.4): Canonical encoding is enforced here let cmu = { - let mut f = [0; 32]; + let mut f = [0u8; 32]; reader.read_exact(&mut f)?; bls12_381::Scalar::from_repr(f) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))? @@ -251,7 +251,7 @@ impl OutputDescription { // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_output() let ephemeral_key = { - let mut bytes = [0; 32]; + let mut bytes = [0u8; 32]; reader.read_exact(&mut bytes)?; let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes); if ephemeral_key.is_none().into() { @@ -263,8 +263,8 @@ impl OutputDescription { ephemeral_key.unwrap() }; - let mut enc_ciphertext = [0; 580]; - let mut out_ciphertext = [0; 80]; + let mut enc_ciphertext = [0u8; 580]; + let mut out_ciphertext = [0u8; 80]; reader.read_exact(&mut enc_ciphertext)?; reader.read_exact(&mut out_ciphertext)?; @@ -272,7 +272,7 @@ impl OutputDescription { // - Canonical encoding is enforced by the API of SaplingVerificationContext::check_output() // due to the need to parse this into a bellman::groth16::Proof. // - Proof validity is enforced in SaplingVerificationContext::check_output() - let mut zkproof = [0; GROTH_PROOF_SIZE]; + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut zkproof)?; Ok(OutputDescription { @@ -350,7 +350,7 @@ impl JSDescription { pub fn read(mut reader: R, use_groth: bool) -> io::Result { // Consensus rule (§4.3): Canonical encoding is enforced here let vpub_old = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_u64_le_bytes(tmp) } @@ -358,7 +358,7 @@ impl JSDescription { // Consensus rule (§4.3): Canonical encoding is enforced here let vpub_new = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_u64_le_bytes(tmp) } @@ -367,16 +367,16 @@ impl JSDescription { // Consensus rule (§4.3): One of vpub_old and vpub_new being zero is // enforced by CheckTransactionWithoutProofVerification() in zcashd. - let mut anchor = [0; 32]; + let mut anchor = [0u8; 32]; reader.read_exact(&mut anchor)?; - let mut nullifiers = [[0; 32]; ZC_NUM_JS_INPUTS]; + let mut nullifiers = [[0u8; 32]; ZC_NUM_JS_INPUTS]; nullifiers .iter_mut() .map(|nf| reader.read_exact(nf)) .collect::>()?; - let mut commitments = [[0; 32]; ZC_NUM_JS_OUTPUTS]; + let mut commitments = [[0u8; 32]; ZC_NUM_JS_OUTPUTS]; commitments .iter_mut() .map(|cm| reader.read_exact(cm)) @@ -384,13 +384,13 @@ impl JSDescription { // Consensus rule (§4.3): Canonical encoding is enforced by // ZCNoteDecryption::decrypt() in zcashd - let mut ephemeral_key = [0; 32]; + let mut ephemeral_key = [0u8; 32]; reader.read_exact(&mut ephemeral_key)?; - let mut random_seed = [0; 32]; + let mut random_seed = [0u8; 32]; reader.read_exact(&mut random_seed)?; - let mut macs = [[0; 32]; ZC_NUM_JS_INPUTS]; + let mut macs = [[0u8; 32]; ZC_NUM_JS_INPUTS]; macs.iter_mut() .map(|mac| reader.read_exact(mac)) .collect::>()?; @@ -399,19 +399,19 @@ impl JSDescription { // Consensus rules (§4.3): // - Canonical encoding is enforced in librustzcash_sprout_verify() // - Proof validity is enforced in librustzcash_sprout_verify() - let mut proof = [0; GROTH_PROOF_SIZE]; + let mut proof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut proof)?; SproutProof::Groth(proof) } else { // Consensus rules (§4.3): // - Canonical encoding is enforced by PHGRProof in zcashd // - Proof validity is enforced by JSDescription::Verify() in zcashd - let mut proof = [0; PHGR_PROOF_SIZE]; + let mut proof = [0u8; PHGR_PROOF_SIZE]; reader.read_exact(&mut proof)?; SproutProof::PHGR(proof) }; - let mut ciphertexts = [[0; 601]; ZC_NUM_JS_OUTPUTS]; + let mut ciphertexts = [[0u8; 601]; ZC_NUM_JS_OUTPUTS]; ciphertexts .iter_mut() .map(|ct| reader.read_exact(ct)) From ca5bc6b0ae6317076cea75f6f2af216f2255a1f1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 21 Aug 2020 18:25:30 +0100 Subject: [PATCH 169/210] zcash_primitives: Fix names of some test vector macros --- zcash_primitives/src/note_encryption.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index b2906d8ec6..a2bff59a83 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1731,13 +1731,13 @@ mod tests { fn test_vectors() { let test_vectors = crate::test_vectors::note_encryption::make_test_vectors(); - macro_rules! read_fr { + macro_rules! read_bls12_381_scalar { ($field:expr) => {{ bls12_381::Scalar::from_repr($field[..].try_into().unwrap()).unwrap() }}; } - macro_rules! read_fs { + macro_rules! read_jubjub_scalar { ($field:expr) => {{ jubjub::Fr::from_repr($field[..].try_into().unwrap()).unwrap() }}; @@ -1757,12 +1757,12 @@ mod tests { // Load the test vector components // - let ivk = read_fs!(tv.ivk); + let ivk = read_jubjub_scalar!(tv.ivk); let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap(); - let rcm = read_fs!(tv.rcm); + let rcm = read_jubjub_scalar!(tv.rcm); let cv = read_point!(tv.cv); - let cmu = read_fr!(tv.cmu); - let esk = read_fs!(tv.esk); + let cmu = read_bls12_381_scalar!(tv.cmu); + let esk = read_jubjub_scalar!(tv.esk); let epk = read_point!(tv.epk).into_subgroup().unwrap(); // From 7a6223202efe0c852ad00378a3c4de317fd1a0ec Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 21 Aug 2020 18:27:09 +0100 Subject: [PATCH 170/210] zcash_primitives: Remove redundant TODO The primitives are no longer generic over a curve; we hard-code Jubjub, so we know that the smallest u-coordinate that is not on the curve is 1. --- zcash_primitives/src/primitives.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 6487dd626b..ba36a7dd80 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -223,9 +223,6 @@ impl Note { pub fn uncommitted() -> bls12_381::Scalar { // 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. bls12_381::Scalar::one() } From d15acf8acc9b5acdd1b1c8655e12bda7c3d9672c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 21 Aug 2020 18:33:22 +0100 Subject: [PATCH 171/210] zcash_primitives: Rename Note::cm to Note::cmu --- zcash_client_backend/src/welding_rig.rs | 2 +- zcash_client_sqlite/src/lib.rs | 6 +++--- zcash_primitives/src/note_encryption.rs | 10 +++++----- zcash_primitives/src/primitives.rs | 2 +- zcash_primitives/src/transaction/builder.rs | 20 ++++++++++---------- zcash_proofs/src/circuit/sapling.rs | 6 +++--- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 7f43cb567f..c654d31ad5 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -263,7 +263,7 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm().to_repr().as_ref().to_owned(); + let cmu = note.cmu().to_repr().as_ref().to_owned(); let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index c0ec73d194..87327ee6a7 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -139,7 +139,7 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm().to_repr().as_ref().to_vec(); + let cmu = note.cmu().to_repr().as_ref().to_vec(); let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); @@ -199,7 +199,7 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm().to_repr().as_ref().to_vec(); + let cmu = note.cmu().to_repr().as_ref().to_vec(); let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); @@ -227,7 +227,7 @@ mod tests { Memo::default(), &mut rng, ); - let cmu = note.cm().to_repr().as_ref().to_vec(); + let cmu = note.cmu().to_repr().as_ref().to_vec(); let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index a2bff59a83..64dd90f3d4 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -212,7 +212,7 @@ pub fn prf_ock( /// }; /// let rcm = jubjub::Fr::random(&mut rng); /// let note = to.create_note(value, Rseed::BeforeZip212(rcm)).unwrap(); -/// let cmu = note.cm(); +/// let cmu = note.cmu(); /// /// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); /// let encCiphertext = enc.encrypt_note_plaintext(); @@ -354,7 +354,7 @@ fn parse_note_plaintext_without_memo( let to = PaymentAddress::from_parts(diversifier, pk_d)?; let note = to.create_note(v, rseed).unwrap(); - if note.cm() != *cmu { + if note.cmu() != *cmu { // Published commitment doesn't match calculated commitment return None; } @@ -552,7 +552,7 @@ pub fn try_sapling_output_recovery_with_ock( let to = PaymentAddress::from_parts(diversifier, pk_d)?; let note = to.create_note(v, rseed).unwrap(); - if note.cm() != *cmu { + if note.cmu() != *cmu { // Published commitment doesn't match calculated commitment return None; } @@ -825,7 +825,7 @@ mod tests { let rseed = generate_random_rseed::(height, &mut rng); let note = pa.create_note(value, rseed).unwrap(); - let cmu = note.cm(); + let cmu = note.cmu(); let ovk = OutgoingViewingKey([0; 32]); let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), &mut rng); @@ -1781,7 +1781,7 @@ mod tests { let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); let note = to.create_note(tv.v, Rseed::BeforeZip212(rcm)).unwrap(); - assert_eq!(note.cm(), cmu); + assert_eq!(note.cmu(), cmu); // // Test decryption diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index ba36a7dd80..7d9aa0533f 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -276,7 +276,7 @@ impl Note { } /// Computes the note commitment - pub fn cm(&self) -> bls12_381::Scalar { + pub fn cmu(&self) -> bls12_381::Scalar { // The commitment is in the prime order subgroup, so mapping the // commitment to the u-coordinate is an injective encoding. jubjub::ExtendedPoint::from(self.cm_full_point()) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9bc3577db2..af579716c3 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -138,7 +138,7 @@ impl SaplingOutput { self.note.value, ); - let cmu = self.note.cm(); + let cmu = self.note.cmu(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu); @@ -368,7 +368,7 @@ impl Builder { merkle_path: MerklePath, ) -> Result<(), Error> { // Consistency check: all anchors must equal the first one - let cm = Node::new(note.cm().into()); + let cm = Node::new(note.cmu().into()); if let Some(anchor) = self.anchor { let path_root: bls12_381::Scalar = merkle_path.root(cm).into(); if path_root != anchor { @@ -635,7 +635,7 @@ impl Builder { dummy_note.value, ); - let cmu = dummy_note.cm(); + let cmu = dummy_note.cmu(); let mut enc_ciphertext = [0u8; 580]; let mut out_ciphertext = [0u8; 80]; @@ -780,9 +780,9 @@ mod tests { let note1 = to .create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm1 = Node::new(note1.cm().to_repr()); + let cmu1 = Node::new(note1.cmu().to_repr()); let mut tree = CommitmentTree::new(); - tree.append(cm1).unwrap(); + tree.append(cmu1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); let mut builder = Builder::::new(0); @@ -879,9 +879,9 @@ mod tests { let note1 = to .create_note(59999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm1 = Node::new(note1.cm().to_repr()); + let cmu1 = Node::new(note1.cmu().to_repr()); let mut tree = CommitmentTree::new(); - tree.append(cm1).unwrap(); + tree.append(cmu1).unwrap(); let mut witness1 = IncrementalWitness::from_tree(&tree); // Fail if there is insufficient input @@ -919,9 +919,9 @@ mod tests { let note2 = to .create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm2 = Node::new(note2.cm().to_repr()); - tree.append(cm2).unwrap(); - witness1.append(cm2).unwrap(); + let cmu2 = Node::new(note2.cmu().to_repr()); + tree.append(cmu2).unwrap(); + witness1.append(cmu2).unwrap(); let witness2 = IncrementalWitness::from_tree(&tree); // Succeeds if there is sufficient input diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 3e0eec78ff..f8d7a65972 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -576,7 +576,7 @@ fn test_input_circuit_with_bls12_381() { }; let mut position = 0u64; - let cm = note.cm(); + let cm = note.cmu(); let mut cur = cm.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { @@ -757,7 +757,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { }; let mut position = 0u64; - let cm = note.cm(); + let cm = note.cmu(); let mut cur = cm.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { @@ -910,7 +910,7 @@ fn test_output_circuit_with_bls12_381() { Rseed::BeforeZip212(commitment_randomness), ) .expect("should be valid") - .cm(); + .cmu(); let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); From 24c61f941757dd1f912b46cc1a7b5400b4b0fd95 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 21 Aug 2020 18:37:51 +0100 Subject: [PATCH 172/210] zcash_proofs: Remove unnecessary comment Likely left over from the Sapling audit. --- zcash_proofs/src/circuit/ecc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 0ca3c0e5e3..212b75afad 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -1040,7 +1040,6 @@ mod test { }; let check_small_order_from_strs = |x, y| { - //let (x,y) = (bls12_381::Scalar::from_str("14080418777298869350588389379361252092475090129841789940098060767181937064268").unwrap(), bls12_381::Scalar::from_str("4408371274642418797323679050836535851651768103477128764103246588657558662748").unwrap()); let (x, y) = ( bls12_381::Scalar::from_str(x).unwrap(), bls12_381::Scalar::from_str(y).unwrap(), From ee6c21f95be8235cd8568d80ff912a67938e31e2 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Fri, 21 Aug 2020 20:27:01 +0100 Subject: [PATCH 173/210] Scripted diff: ``` find . -not -path '*/.git/*' -a -type f -exec sed -i -e 's/hash_x/hash_u/g' {} \; find . -not -path '*/.git/*' -a -type f -exec sed -i -e 's/hash_y/hash_v/g' {} \; ``` Signed-off-by: Daira Hopwood --- zcash_primitives/src/pedersen_hash.rs | 8 +- .../src/test_vectors/pedersen_hash_vectors.rs | 148 +++++++++--------- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index edbc5344bb..6f5160f702 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -129,8 +129,8 @@ pub mod test { pub struct TestVector<'a> { pub personalization: Personalization, pub input_bits: Vec, - pub hash_x: &'a str, - pub hash_y: &'a str, + pub hash_u: &'a str, + pub hash_v: &'a str, } #[test] @@ -151,8 +151,8 @@ pub mod test { )) .to_affine(); - assert_eq!(p.get_u().to_string(), v.hash_x); - assert_eq!(p.get_v().to_string(), v.hash_y); + assert_eq!(p.get_u().to_string(), v.hash_u); + assert_eq!(p.get_v().to_string(), v.hash_v); } } } diff --git a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs index eb71175cf4..c638f74861 100644 --- a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs +++ b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs @@ -7,26 +7,26 @@ pub fn get_vectors<'a>() -> Vec> { TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1], - hash_x: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b", - hash_y: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982", + hash_u: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b", + hash_v: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 0], - hash_x: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a", - hash_y: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638", + hash_u: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a", + hash_v: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1], - hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", - hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", + hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0], - hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", - hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", + hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, @@ -39,8 +39,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, ], - hash_x: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f", - hash_y: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527", + hash_u: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f", + hash_v: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527", }, TestVector { personalization: Personalization::NoteCommitment, @@ -53,8 +53,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd", - hash_y: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2", + hash_u: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd", + hash_v: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2", }, TestVector { personalization: Personalization::NoteCommitment, @@ -67,8 +67,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, ], - hash_x: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40", - hash_y: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743", + hash_u: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40", + hash_v: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743", }, TestVector { personalization: Personalization::NoteCommitment, @@ -101,8 +101,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, ], - hash_x: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27", - hash_y: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74", + hash_u: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27", + hash_v: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74", }, TestVector { personalization: Personalization::NoteCommitment, @@ -136,8 +136,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, ], - hash_x: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e", - hash_y: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab", + hash_u: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e", + hash_v: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab", }, TestVector { personalization: Personalization::NoteCommitment, @@ -177,8 +177,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, ], - hash_x: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f", - hash_y: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71", + hash_u: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f", + hash_v: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71", }, TestVector { personalization: Personalization::NoteCommitment, @@ -218,32 +218,32 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, ], - hash_x: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7", - hash_y: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f", + hash_u: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7", + hash_v: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0], - hash_x: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d", - hash_y: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330", + hash_u: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d", + hash_v: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 0], - hash_x: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea", - hash_y: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4", + hash_u: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea", + hash_v: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1], - hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", - hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", + hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0], - hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", - hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", + hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -256,8 +256,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, ], - hash_x: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd", - hash_y: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544", + hash_u: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd", + hash_v: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -270,8 +270,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, ], - hash_x: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555", - hash_y: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb", + hash_u: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555", + hash_v: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -284,8 +284,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, ], - hash_x: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d", - hash_y: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f", + hash_u: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d", + hash_v: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -318,8 +318,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, ], - hash_x: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85", - hash_y: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a", + hash_u: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85", + hash_v: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -353,8 +353,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, ], - hash_x: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68", - hash_y: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee", + hash_u: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68", + hash_v: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -394,8 +394,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, ], - hash_x: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37", - hash_y: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6", + hash_u: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37", + hash_v: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -435,32 +435,32 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05", - hash_y: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33", + hash_u: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05", + hash_v: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1], - hash_x: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a", - hash_y: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a", + hash_u: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a", + hash_v: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 0], - hash_x: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c", - hash_y: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df", + hash_u: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c", + hash_v: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1], - hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", - hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", + hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0], - hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", - hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", + hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -473,8 +473,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, ], - hash_x: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2", - hash_y: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31", + hash_u: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2", + hash_v: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -487,8 +487,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, ], - hash_x: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb", - hash_y: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f", + hash_u: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb", + hash_v: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -501,8 +501,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, ], - hash_x: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2", - hash_y: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de", + hash_u: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2", + hash_v: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -535,8 +535,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, ], - hash_x: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc", - hash_y: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de", + hash_u: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc", + hash_v: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -570,8 +570,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, ], - hash_x: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda", - hash_y: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d", + hash_u: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda", + hash_v: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -611,8 +611,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, ], - hash_x: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666", - hash_y: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289", + hash_u: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666", + hash_v: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -652,8 +652,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, ], - hash_x: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7", - hash_y: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad", + hash_u: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7", + hash_v: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad", }, TestVector { personalization: Personalization::MerkleTree(27), @@ -666,8 +666,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, ], - hash_x: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4", - hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", + hash_u: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4", + hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, TestVector { personalization: Personalization::MerkleTree(36), @@ -680,8 +680,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, ], - hash_x: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024", - hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", + hash_u: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024", + hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -694,8 +694,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - hash_x: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd", - hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", + hash_u: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd", + hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::NoteCommitment, @@ -708,8 +708,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ], - hash_x: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d", - hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", + hash_u: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d", + hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, ]; } From 20c5096793fcd65c5f92bb1648303b38c1ac0cd1 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Fri, 21 Aug 2020 20:43:19 +0100 Subject: [PATCH 174/210] Fix remaining cases of cm -> cmu. Signed-off-by: Daira Hopwood --- zcash_primitives/src/merkle_tree.rs | 14 +++++++------- zcash_primitives/src/transaction/builder.rs | 6 +++--- zcash_proofs/src/circuit/sapling.rs | 16 ++++++++-------- zcash_proofs/src/sapling/verifier.rs | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index ec9103d196..15550cf494 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -1005,19 +1005,19 @@ mod tests { assert_eq!(tree.size(), 0); let mut witnesses = vec![]; - let mut last_cm = None; + let mut last_cmu = None; let mut paths_i = 0; let mut witness_ser_i = 0; for i in 0..16 { - let cm = hex::decode(commitments[i]).unwrap(); + let cmu = hex::decode(commitments[i]).unwrap(); - let cm = Node::new(cm[..].try_into().unwrap()); + let cmu = Node::new(cmu[..].try_into().unwrap()); // Witness here - witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm)); + witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cmu)); // Now append a commitment to the tree - assert!(tree.append(cm).is_ok()); + assert!(tree.append(cmu).is_ok()); // Size incremented by one. assert_eq!(tree.size(), i + 1); @@ -1030,7 +1030,7 @@ mod tests { for (witness, leaf) in witnesses.as_mut_slice() { // Append the same commitment to all the witnesses - assert!(witness.append(cm).is_ok()); + assert!(witness.append(cmu).is_ok()); if let Some(leaf) = leaf { let path = witness.path().expect("should be able to create a path"); @@ -1054,7 +1054,7 @@ mod tests { assert_eq!(witness.root(), tree.root()); } - last_cm = Some(cm); + last_cmu = Some(cmu); } // Tree should be full now diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index af579716c3..d6c2ea5965 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -368,14 +368,14 @@ impl Builder { merkle_path: MerklePath, ) -> Result<(), Error> { // Consistency check: all anchors must equal the first one - let cm = Node::new(note.cmu().into()); + let cmu = Node::new(note.cmu().into()); if let Some(anchor) = self.anchor { - let path_root: bls12_381::Scalar = merkle_path.root(cm).into(); + let path_root: bls12_381::Scalar = merkle_path.root(cmu).into(); if path_root != anchor { return Err(Error::AnchorMismatch); } } else { - self.anchor = Some(merkle_path.root(cm).into()) + self.anchor = Some(merkle_path.root(cmu).into()) } let alpha = jubjub::Fr::random(&mut self.rng); diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index f8d7a65972..1aa2cfc966 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -576,8 +576,8 @@ fn test_input_circuit_with_bls12_381() { }; let mut position = 0u64; - let cm = note.cmu(); - let mut cur = cm.clone(); + let cmu = note.cmu(); + let mut cur = cmu.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { let (uncle, b) = val.unwrap(); @@ -635,7 +635,7 @@ fn test_input_circuit_with_bls12_381() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + assert_eq!(cs.get("randomization of note commitment/x3/num"), cmu); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); @@ -757,8 +757,8 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { }; let mut position = 0u64; - let cm = note.cmu(); - let mut cur = cm.clone(); + let cmu = note.cmu(); + let mut cur = cmu.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { let (uncle, b) = val.unwrap(); @@ -816,7 +816,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + assert_eq!(cs.get("randomization of note commitment/x3/num"), cmu); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); @@ -904,7 +904,7 @@ fn test_output_circuit_with_bls12_381() { "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee" ); - let expected_cm = payment_address + let expected_cmu = payment_address .create_note( value_commitment.value, Rseed::BeforeZip212(commitment_randomness), @@ -936,7 +936,7 @@ fn test_output_circuit_with_bls12_381() { cs.get_input(4, "epk/y/input variable"), expected_epk.get_v() ); - assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); + assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cmu); } } } diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index 37bf1be3d2..d30ec9b215 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -95,7 +95,7 @@ impl SaplingVerificationContext { pub fn check_output( &mut self, cv: jubjub::ExtendedPoint, - cm: bls12_381::Scalar, + cmu: bls12_381::Scalar, epk: jubjub::ExtendedPoint, zkproof: Proof, verifying_key: &PreparedVerifyingKey, @@ -121,7 +121,7 @@ impl SaplingVerificationContext { public_input[2] = x; public_input[3] = y; } - public_input[4] = cm; + public_input[4] = cmu; // Verify the proof verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() From cfed47c17643b9c5c8e482c0bdf059554d883187 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sat, 22 Aug 2020 00:41:49 +0100 Subject: [PATCH 175/210] Rename ValueCommitment.cm() to .commitment() (because it's confusing to have cm referring to both note and value commitments). Signed-off-by: Daira Hopwood --- zcash_primitives/src/note_encryption.rs | 4 +-- zcash_primitives/src/primitives.rs | 2 +- zcash_primitives/src/prover.rs | 4 +-- zcash_proofs/src/circuit/sapling.rs | 33 ++++++++++++++----------- zcash_proofs/src/sapling/prover.rs | 6 ++--- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 64dd90f3d4..6e120fe84a 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -216,7 +216,7 @@ pub fn prf_ock( /// /// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); /// let encCiphertext = enc.encrypt_note_plaintext(); -/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm().into(), &cmu); +/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu); /// ``` pub struct SaplingNoteEncryption { epk: jubjub::SubgroupPoint, @@ -820,7 +820,7 @@ mod tests { value, randomness: jubjub::Fr::random(&mut rng), }; - let cv = value_commitment.cm().into(); + let cv = value_commitment.commitment().into(); let rseed = generate_random_rseed::(height, &mut rng); diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 7d9aa0533f..79e1b0d8e1 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -25,7 +25,7 @@ pub struct ValueCommitment { } impl ValueCommitment { - pub fn cm(&self) -> jubjub::SubgroupPoint { + pub fn commitment(&self) -> jubjub::SubgroupPoint { (constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value)) + (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness) } diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 1349d3b552..83cf4d4d12 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -103,7 +103,7 @@ pub(crate) mod mock { value, randomness: jubjub::Fr::random(&mut rng), } - .cm() + .commitment() .into(); let rk = PublicKey(proof_generation_key.ak.clone().into()) @@ -126,7 +126,7 @@ pub(crate) mod mock { value, randomness: jubjub::Fr::random(&mut rng), } - .cm() + .commitment() .into(); ([0u8; GROTH_PROOF_SIZE], cv) diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 1aa2cfc966..b7d86cc34f 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -567,7 +567,8 @@ fn test_input_circuit_with_bls12_381() { { let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); - let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); let note = Note { value: value_commitment.value, g_d: g_d.clone(), @@ -643,11 +644,11 @@ fn test_input_circuit_with_bls12_381() { assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); assert_eq!( cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.get_u() + expected_value_commitment.get_u() ); assert_eq!( cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.get_v() + expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -675,7 +676,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let tree_depth = 32; - let expected_cm_xs = vec![ + let expected_commitment_xs = vec![ "43821661663052659750276289184181083197337192946256245809816728673021647664276", "7220807656052227578299730541645543434083158611414003423211850718229633594616", "13239753550660714843257636471668037031928211668773449453628093339627668081697", @@ -688,7 +689,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "18269767207277008186871145355531741929166733260352590789136389380124992250945", ]; - let expected_cm_ys = vec![ + let expected_commitment_ys = vec![ "27630722367128086497290371604583225252915685718989450292520883698391703910", "23310648738313092772044712773481584369462075017189681529702825235349449805260", "25709635353183537915646348052945798827495141780341329896098121888376871589480", @@ -740,14 +741,15 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { { let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); - let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); assert_eq!( - expected_value_cm.get_u(), - bls12_381::Scalar::from_str(&expected_cm_xs[i as usize]).unwrap() + expected_value_commitment.get_u(), + bls12_381::Scalar::from_str(&expected_commitment_xs[i as usize]).unwrap() ); assert_eq!( - expected_value_cm.get_v(), - bls12_381::Scalar::from_str(&expected_cm_ys[i as usize]).unwrap() + expected_value_commitment.get_v(), + bls12_381::Scalar::from_str(&expected_commitment_ys[i as usize]).unwrap() ); let note = Note { value: value_commitment.value, @@ -824,11 +826,11 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); assert_eq!( cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.get_u() + expected_value_commitment.get_u() ); assert_eq!( cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.get_v() + expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -912,7 +914,8 @@ fn test_output_circuit_with_bls12_381() { .expect("should be valid") .cmu(); - let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); let expected_epk = jubjub::ExtendedPoint::from(payment_address.g_d().expect("should be valid") * esk) @@ -922,11 +925,11 @@ fn test_output_circuit_with_bls12_381() { assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); assert_eq!( cs.get_input(1, "value commitment/commitment point/x/input variable"), - expected_value_cm.get_u() + expected_value_commitment.get_u() ); assert_eq!( cs.get_input(2, "value commitment/commitment point/y/input variable"), - expected_value_cm.get_v() + expected_value_commitment.get_v() ); assert_eq!( cs.get_input(3, "epk/x/input variable"), diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 7cf4267dc8..8821a57e67 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -120,7 +120,7 @@ impl SaplingProvingContext { public_input[1] = y; } { - let affine = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine(); + let affine = jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); let (x, y) = (affine.get_u(), affine.get_v()); public_input[2] = x; public_input[3] = y; @@ -142,7 +142,7 @@ impl SaplingProvingContext { verify_proof(verifying_key, &proof, &public_input[..]).map_err(|_| ())?; // Compute value commitment - let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.commitment().into(); // Accumulate the value commitment in the context self.cv_sum += value_commitment; @@ -197,7 +197,7 @@ impl SaplingProvingContext { create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail"); // Compute the actual value commitment - let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.commitment().into(); // Accumulate the value commitment in the context. We do this to check internal consistency. self.cv_sum -= value_commitment; // Outputs subtract from the total. From 9e0041c497c52f70d2e9c7a848cacf456e231c21 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sat, 22 Aug 2020 01:01:05 +0100 Subject: [PATCH 176/210] Consistently use (u, v) for affine-ctEdwards coordinates. Signed-off-by: Daira Hopwood --- zcash_proofs/src/circuit/ecc.rs | 375 +++++++++++----------- zcash_proofs/src/circuit/pedersen_hash.rs | 20 +- zcash_proofs/src/circuit/sapling.rs | 60 ++-- zcash_proofs/src/sapling/prover.rs | 12 +- zcash_proofs/src/sapling/verifier.rs | 24 +- 5 files changed, 248 insertions(+), 243 deletions(-) diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 212b75afad..933764366a 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -18,8 +18,8 @@ use crate::constants::{FixedGenerator, EDWARDS_D, MONTGOMERY_A, MONTGOMERY_SCALE #[derive(Clone)] pub struct EdwardsPoint { - x: AllocatedNum, - y: AllocatedNum, + u: AllocatedNum, + v: AllocatedNum, } /// Perform a fixed-base scalar multiplication with @@ -49,13 +49,14 @@ where .cloned() .unwrap_or_else(|| Boolean::constant(false)); - let (x, y) = lookup3_xy( + // TODO: rename to lookup3_uv + let (u, v) = lookup3_xy( cs.namespace(|| format!("window table lookup {}", i)), &[chunk_a, chunk_b, chunk_c], window, )?; - let p = EdwardsPoint { x, y }; + let p = EdwardsPoint { u, v }; if result.is_none() { result = Some(p); @@ -72,12 +73,12 @@ where } impl EdwardsPoint { - pub fn get_x(&self) -> &AllocatedNum { - &self.x + pub fn get_u(&self) -> &AllocatedNum { + &self.u } - pub fn get_y(&self) -> &AllocatedNum { - &self.y + pub fn get_v(&self) -> &AllocatedNum { + &self.v } pub fn assert_not_small_order(&self, mut cs: CS) -> Result<(), SynthesisError> @@ -90,9 +91,9 @@ impl EdwardsPoint { // (0, -1) is a small order point, but won't ever appear here // because cofactor is 2^3, and we performed three doublings. - // (0, 1) is the neutral element, so checking if x is nonzero + // (0, 1) is the neutral element, so checking if u is nonzero // is sufficient to prevent small order points here. - tmp.x.assert_nonzero(cs.namespace(|| "check x != 0"))?; + tmp.u.assert_nonzero(cs.namespace(|| "check u != 0"))?; Ok(()) } @@ -101,8 +102,8 @@ impl EdwardsPoint { where CS: ConstraintSystem, { - self.x.inputize(cs.namespace(|| "x"))?; - self.y.inputize(cs.namespace(|| "y"))?; + self.u.inputize(cs.namespace(|| "u"))?; + self.v.inputize(cs.namespace(|| "v"))?; Ok(()) } @@ -114,12 +115,12 @@ impl EdwardsPoint { { let mut tmp = vec![]; - let x = self.x.to_bits_le_strict(cs.namespace(|| "unpack x"))?; + let u = self.u.to_bits_le_strict(cs.namespace(|| "unpack u"))?; - let y = self.y.to_bits_le_strict(cs.namespace(|| "unpack y"))?; + let v = self.v.to_bits_le_strict(cs.namespace(|| "unpack v"))?; - tmp.extend(y); - tmp.push(x[0].clone()); + tmp.extend(v); + tmp.push(u[0].clone()); Ok(tmp) } @@ -132,13 +133,13 @@ impl EdwardsPoint { { let p = p.map(|p| p.to_affine()); - // Allocate x - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(p.get()?.get_u()))?; + // Allocate u + let u = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(p.get()?.get_u()))?; - // Allocate y - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(p.get()?.get_v()))?; + // Allocate v + let v = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(p.get()?.get_v()))?; - Self::interpret(cs.namespace(|| "point interpretation"), &x, &y) + Self::interpret(cs.namespace(|| "point interpretation"), &u, &v) } /// Returns `self` if condition is true, and the neutral @@ -151,48 +152,48 @@ impl EdwardsPoint { where CS: ConstraintSystem, { - // Compute x' = self.x if condition, and 0 otherwise - let x_prime = AllocatedNum::alloc(cs.namespace(|| "x'"), || { + // Compute u' = self.u if condition, and 0 otherwise + let u_prime = AllocatedNum::alloc(cs.namespace(|| "u'"), || { if *condition.get_value().get()? { - Ok(*self.x.get_value().get()?) + Ok(*self.u.get_value().get()?) } else { Ok(bls12_381::Scalar::zero()) } })?; - // condition * x = x' - // if condition is 0, x' must be 0 - // if condition is 1, x' must be x + // condition * u = u' + // if condition is 0, u' must be 0 + // if condition is 1, u' must be u let one = CS::one(); cs.enforce( - || "x' computation", - |lc| lc + self.x.get_variable(), + || "u' computation", + |lc| lc + self.u.get_variable(), |_| condition.lc(one, bls12_381::Scalar::one()), - |lc| lc + x_prime.get_variable(), + |lc| lc + u_prime.get_variable(), ); - // Compute y' = self.y if condition, and 1 otherwise - let y_prime = AllocatedNum::alloc(cs.namespace(|| "y'"), || { + // Compute v' = self.v if condition, and 1 otherwise + let v_prime = AllocatedNum::alloc(cs.namespace(|| "v'"), || { if *condition.get_value().get()? { - Ok(*self.y.get_value().get()?) + Ok(*self.v.get_value().get()?) } else { Ok(bls12_381::Scalar::one()) } })?; - // condition * y = y' - (1 - condition) - // if condition is 0, y' must be 1 - // if condition is 1, y' must be y + // condition * v = v' - (1 - condition) + // if condition is 0, v' must be 1 + // if condition is 1, v' must be v cs.enforce( - || "y' computation", - |lc| lc + self.y.get_variable(), + || "v' computation", + |lc| lc + self.v.get_variable(), |_| condition.lc(one, bls12_381::Scalar::one()), - |lc| lc + y_prime.get_variable() - &condition.not().lc(one, bls12_381::Scalar::one()), + |lc| lc + v_prime.get_variable() - &condition.not().lc(one, bls12_381::Scalar::one()), ); Ok(EdwardsPoint { - x: x_prime, - y: y_prime, + u: u_prime, + v: v_prime, }) } @@ -248,29 +249,29 @@ impl EdwardsPoint { pub fn interpret( mut cs: CS, - x: &AllocatedNum, - y: &AllocatedNum, + u: &AllocatedNum, + v: &AllocatedNum, ) -> Result where CS: ConstraintSystem, { - // -x^2 + y^2 = 1 + dx^2y^2 + // -u^2 + v^2 = 1 + du^2v^2 - let x2 = x.square(cs.namespace(|| "x^2"))?; - let y2 = y.square(cs.namespace(|| "y^2"))?; - let x2y2 = x2.mul(cs.namespace(|| "x^2 y^2"), &y2)?; + let u2 = u.square(cs.namespace(|| "u^2"))?; + let v2 = v.square(cs.namespace(|| "v^2"))?; + let u2v2 = u2.mul(cs.namespace(|| "u^2 v^2"), &v2)?; let one = CS::one(); cs.enforce( || "on curve check", - |lc| lc - x2.get_variable() + y2.get_variable(), + |lc| lc - u2.get_variable() + v2.get_variable(), |lc| lc + one, - |lc| lc + one + (EDWARDS_D, x2y2.get_variable()), + |lc| lc + one + (EDWARDS_D, u2v2.get_variable()), ); Ok(EdwardsPoint { - x: x.clone(), - y: y.clone(), + u: u.clone(), + v: v.clone(), }) } @@ -278,13 +279,14 @@ impl EdwardsPoint { where CS: ConstraintSystem, { - // Compute T = (x1 + y1) * (x1 + y1) + // Compute T = (u + v) * (v - EDWARDS_A*u) + // = (u + v) * (u + v) let t = AllocatedNum::alloc(cs.namespace(|| "T"), || { - let mut t0 = *self.x.get_value().get()?; - t0.add_assign(self.y.get_value().get()?); + let mut t0 = *self.u.get_value().get()?; + t0.add_assign(self.v.get_value().get()?); - let mut t1 = *self.x.get_value().get()?; - t1.add_assign(self.y.get_value().get()?); + let mut t1 = *self.u.get_value().get()?; + t1.add_assign(self.v.get_value().get()?); t0.mul_assign(&t1); @@ -293,13 +295,13 @@ impl EdwardsPoint { cs.enforce( || "T computation", - |lc| lc + self.x.get_variable() + self.y.get_variable(), - |lc| lc + self.x.get_variable() + self.y.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), |lc| lc + t.get_variable(), ); - // Compute A = x1 * y1 - let a = self.x.mul(cs.namespace(|| "A computation"), &self.y)?; + // Compute A = u * v + let a = self.u.mul(cs.namespace(|| "A computation"), &self.v)?; // Compute C = d*A*A let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { @@ -316,8 +318,8 @@ impl EdwardsPoint { |lc| lc + c.get_variable(), ); - // Compute x3 = (2.A) / (1 + C) - let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + // Compute u3 = (2.A) / (1 + C) + let u3 = AllocatedNum::alloc(cs.namespace(|| "u3"), || { let mut t0 = *a.get_value().get()?; t0 = t0.double(); @@ -334,14 +336,15 @@ impl EdwardsPoint { let one = CS::one(); cs.enforce( - || "x3 computation", + || "u3 computation", |lc| lc + one + c.get_variable(), - |lc| lc + x3.get_variable(), + |lc| lc + u3.get_variable(), |lc| lc + a.get_variable() + a.get_variable(), ); - // Compute y3 = (U - 2.A) / (1 - C) - let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { + // Compute v3 = (T + (EDWARDS_A-1)*A) / (1 - C) + // = (T - 2.A) / (1 - C) + let v3 = AllocatedNum::alloc(cs.namespace(|| "v3"), || { let mut t0 = *a.get_value().get()?; t0 = t0.double().neg(); t0.add_assign(t.get_value().get()?); @@ -358,13 +361,13 @@ impl EdwardsPoint { })?; cs.enforce( - || "y3 computation", + || "v3 computation", |lc| lc + one - c.get_variable(), - |lc| lc + y3.get_variable(), + |lc| lc + v3.get_variable(), |lc| lc + t.get_variable() - a.get_variable() - a.get_variable(), ); - Ok(EdwardsPoint { x: x3, y: y3 }) + Ok(EdwardsPoint { u: u3, v: v3 }) } /// Perform addition between any two points @@ -372,13 +375,15 @@ impl EdwardsPoint { where CS: ConstraintSystem, { - // Compute U = (x1 + y1) * (x2 + y2) - let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { - let mut t0 = *self.x.get_value().get()?; - t0.add_assign(self.y.get_value().get()?); + // Compute U = (u1 + v1) * (v2 - EDWARDS_A*u2) + // = (u1 + v1) * (u2 + v2) + // (In hindsight, U was a poor choice of name.) + let uppercase_u = AllocatedNum::alloc(cs.namespace(|| "U"), || { + let mut t0 = *self.u.get_value().get()?; + t0.add_assign(self.v.get_value().get()?); - let mut t1 = *other.x.get_value().get()?; - t1.add_assign(other.y.get_value().get()?); + let mut t1 = *other.u.get_value().get()?; + t1.add_assign(other.v.get_value().get()?); t0.mul_assign(&t1); @@ -387,16 +392,16 @@ impl EdwardsPoint { cs.enforce( || "U computation", - |lc| lc + self.x.get_variable() + self.y.get_variable(), - |lc| lc + other.x.get_variable() + other.y.get_variable(), - |lc| lc + u.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), + |lc| lc + other.u.get_variable() + other.v.get_variable(), + |lc| lc + uppercase_u.get_variable(), ); - // Compute A = y2 * x1 - let a = other.y.mul(cs.namespace(|| "A computation"), &self.x)?; + // Compute A = v2 * u1 + let a = other.v.mul(cs.namespace(|| "A computation"), &self.u)?; - // Compute B = x2 * y1 - let b = other.x.mul(cs.namespace(|| "B computation"), &self.y)?; + // Compute B = u2 * v1 + let b = other.u.mul(cs.namespace(|| "B computation"), &self.v)?; // Compute C = d*A*B let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { @@ -414,8 +419,8 @@ impl EdwardsPoint { |lc| lc + c.get_variable(), ); - // Compute x3 = (A + B) / (1 + C) - let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + // Compute u3 = (A + B) / (1 + C) + let u3 = AllocatedNum::alloc(cs.namespace(|| "u3"), || { let mut t0 = *a.get_value().get()?; t0.add_assign(b.get_value().get()?); @@ -432,15 +437,15 @@ impl EdwardsPoint { let one = CS::one(); cs.enforce( - || "x3 computation", + || "u3 computation", |lc| lc + one + c.get_variable(), - |lc| lc + x3.get_variable(), + |lc| lc + u3.get_variable(), |lc| lc + a.get_variable() + b.get_variable(), ); - // Compute y3 = (U - A - B) / (1 - C) - let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { - let mut t0 = *u.get_value().get()?; + // Compute v3 = (U - A - B) / (1 - C) + let v3 = AllocatedNum::alloc(cs.namespace(|| "v3"), || { + let mut t0 = *uppercase_u.get_value().get()?; t0.sub_assign(a.get_value().get()?); t0.sub_assign(b.get_value().get()?); @@ -456,13 +461,13 @@ impl EdwardsPoint { })?; cs.enforce( - || "y3 computation", + || "v3 computation", |lc| lc + one - c.get_variable(), - |lc| lc + y3.get_variable(), - |lc| lc + u.get_variable() - a.get_variable() - b.get_variable(), + |lc| lc + v3.get_variable(), + |lc| lc + uppercase_u.get_variable() - a.get_variable() - b.get_variable(), ); - Ok(EdwardsPoint { x: x3, y: y3 }) + Ok(EdwardsPoint { u: u3, v: v3 }) } } @@ -522,7 +527,7 @@ impl MontgomeryPoint { |lc| lc + &self.x.lc(bls12_381::Scalar::one()) - one, ); - Ok(EdwardsPoint { x: u, y: v }) + Ok(EdwardsPoint { u, v }) } /// Interprets an (x, y) pair as a point @@ -649,8 +654,8 @@ mod test { let q = p.into_edwards(&mut cs).unwrap(); assert!(cs.is_satisfied()); - assert!(q.x.get_value().unwrap() == u); - assert!(q.y.get_value().unwrap() == v); + assert!(q.u.get_value().unwrap() == u); + assert!(q.v.get_value().unwrap() == v); cs.set("u/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied().unwrap(), "u computation"); @@ -680,35 +685,35 @@ mod test { let p = p.to_affine(); assert!(cs.is_satisfied()); - assert_eq!(q.x.get_value().unwrap(), p.get_u()); - assert_eq!(q.y.get_value().unwrap(), p.get_v()); + assert_eq!(q.u.get_value().unwrap(), p.get_u()); + assert_eq!(q.v.get_value().unwrap(), p.get_v()); } for _ in 0..100 { let p = jubjub::ExtendedPoint::random(rng).to_affine(); - let (x, y) = (p.get_u(), p.get_v()); + let (u, v) = (p.get_u(), p.get_v()); let mut cs = TestConstraintSystem::new(); - let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); - let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); + let numu = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(u)).unwrap(); + let numv = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(v)).unwrap(); - let p = EdwardsPoint::interpret(&mut cs, &numx, &numy).unwrap(); + let p = EdwardsPoint::interpret(&mut cs, &numu, &numv).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(p.x.get_value().unwrap(), x); - assert_eq!(p.y.get_value().unwrap(), y); + assert_eq!(p.u.get_value().unwrap(), u); + assert_eq!(p.v.get_value().unwrap(), v); } - // Random (x, y) are unlikely to be on the curve. + // Random (u, v) are unlikely to be on the curve. for _ in 0..100 { - let x = bls12_381::Scalar::random(rng); - let y = bls12_381::Scalar::random(rng); + let u = bls12_381::Scalar::random(rng); + let v = bls12_381::Scalar::random(rng); let mut cs = TestConstraintSystem::new(); - let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); - let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); + let numu = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(u)).unwrap(); + let numv = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(v)).unwrap(); - EdwardsPoint::interpret(&mut cs, &numx, &numy).unwrap(); + EdwardsPoint::interpret(&mut cs, &numu, &numv).unwrap(); assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); } @@ -727,7 +732,7 @@ mod test { let p = zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR; let s = jubjub::Fr::random(rng); let q = jubjub::ExtendedPoint::from(p * s).to_affine(); - let (x1, y1) = (q.get_u(), q.get_v()); + let (u1, v1) = (q.get_u(), q.get_v()); let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); @@ -750,8 +755,8 @@ mod test { ) .unwrap(); - assert_eq!(q.x.get_value().unwrap(), x1); - assert_eq!(q.y.get_value().unwrap(), y1); + assert_eq!(q.u.get_value().unwrap(), u1); + assert_eq!(q.v.get_value().unwrap(), v1); } } @@ -770,15 +775,15 @@ mod test { let q = (p * s).to_affine(); let p = p.to_affine(); - let (x0, y0) = (p.get_u(), p.get_v()); - let (x1, y1) = (q.get_u(), q.get_v()); + let (u0, v0) = (p.get_u(), p.get_v()); + let (u1, v1) = (q.get_u(), q.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); @@ -799,9 +804,9 @@ mod test { assert!(cs.is_satisfied()); - assert_eq!(q.x.get_value().unwrap(), x1); + assert_eq!(q.u.get_value().unwrap(), u1); - assert_eq!(q.y.get_value().unwrap(), y1); + assert_eq!(q.v.get_value().unwrap(), v1); } } @@ -817,14 +822,14 @@ mod test { let p = jubjub::ExtendedPoint::random(rng).to_affine(); - let (x0, y0) = (p.get_u(), p.get_v()); + let (u0, v0) = (p.get_u(), p.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let mut should_we_select = rng.next_u32() % 2 != 0; @@ -852,21 +857,21 @@ mod test { assert!(cs.is_satisfied()); if should_we_select { - assert_eq!(q.x.get_value().unwrap(), x0); - assert_eq!(q.y.get_value().unwrap(), y0); + assert_eq!(q.u.get_value().unwrap(), u0); + assert_eq!(q.v.get_value().unwrap(), v0); - cs.set("select/y'/num", bls12_381::Scalar::one()); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); - cs.set("select/x'/num", bls12_381::Scalar::zero()); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + cs.set("select/v'/num", bls12_381::Scalar::one()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/v' computation"); + cs.set("select/u'/num", bls12_381::Scalar::zero()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/u' computation"); } else { - assert_eq!(q.x.get_value().unwrap(), bls12_381::Scalar::zero()); - assert_eq!(q.y.get_value().unwrap(), bls12_381::Scalar::one()); + assert_eq!(q.u.get_value().unwrap(), bls12_381::Scalar::zero()); + assert_eq!(q.v.get_value().unwrap(), bls12_381::Scalar::one()); - cs.set("select/y'/num", x0); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); - cs.set("select/x'/num", y0); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + cs.set("select/v'/num", u0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/v' computation"); + cs.set("select/u'/num", v0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/u' computation"); } } } @@ -888,51 +893,51 @@ mod test { let p2 = p2.to_affine(); let p3 = p3.to_affine(); - let (x0, y0) = (p1.get_u(), p1.get_v()); - let (x1, y1) = (p2.get_u(), p2.get_v()); - let (x2, y2) = (p3.get_u(), p3.get_v()); + let (u0, v0) = (p1.get_u(), p1.get_v()); + let (u1, v1) = (p2.get_u(), p2.get_v()); + let (u2, v2) = (p3.get_u(), p3.get_v()); let mut cs = TestConstraintSystem::new(); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); - let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); - let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); + let num_u1 = AllocatedNum::alloc(cs.namespace(|| "u1"), || Ok(u1)).unwrap(); + let num_v1 = AllocatedNum::alloc(cs.namespace(|| "v1"), || Ok(v1)).unwrap(); let p1 = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let p2 = EdwardsPoint { - x: num_x1, - y: num_y1, + u: num_u1, + v: num_v1, }; let p3 = p1.add(cs.namespace(|| "addition"), &p2).unwrap(); assert!(cs.is_satisfied()); - assert!(p3.x.get_value().unwrap() == x2); - assert!(p3.y.get_value().unwrap() == y2); + assert!(p3.u.get_value().unwrap() == u2); + assert!(p3.v.get_value().unwrap() == v2); - let u = cs.get("addition/U/num"); + let uppercase_u = cs.get("addition/U/num"); cs.set("addition/U/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/U computation")); - cs.set("addition/U/num", u); + cs.set("addition/U/num", uppercase_u); assert!(cs.is_satisfied()); - let x3 = cs.get("addition/x3/num"); - cs.set("addition/x3/num", bls12_381::Scalar::random(rng)); - assert_eq!(cs.which_is_unsatisfied(), Some("addition/x3 computation")); - cs.set("addition/x3/num", x3); + let u3 = cs.get("addition/u3/num"); + cs.set("addition/u3/num", bls12_381::Scalar::random(rng)); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/u3 computation")); + cs.set("addition/u3/num", u3); assert!(cs.is_satisfied()); - let y3 = cs.get("addition/y3/num"); - cs.set("addition/y3/num", bls12_381::Scalar::random(rng)); - assert_eq!(cs.which_is_unsatisfied(), Some("addition/y3 computation")); - cs.set("addition/y3/num", y3); + let v3 = cs.get("addition/v3/num"); + cs.set("addition/v3/num", bls12_381::Scalar::random(rng)); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/v3 computation")); + cs.set("addition/v3/num", v3); assert!(cs.is_satisfied()); } } @@ -951,25 +956,25 @@ mod test { let p1 = p1.to_affine(); let p2 = p2.to_affine(); - let (x0, y0) = (p1.get_u(), p1.get_v()); - let (x1, y1) = (p2.get_u(), p2.get_v()); + let (u0, v0) = (p1.get_u(), p1.get_v()); + let (u1, v1) = (p2.get_u(), p2.get_v()); let mut cs = TestConstraintSystem::new(); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p1 = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let p2 = p1.double(cs.namespace(|| "doubling")).unwrap(); assert!(cs.is_satisfied()); - assert!(p2.x.get_value().unwrap() == x1); - assert!(p2.y.get_value().unwrap() == y1); + assert!(p2.u.get_value().unwrap() == u1); + assert!(p2.v.get_value().unwrap() == v1); } } @@ -1039,12 +1044,12 @@ mod test { assert!(p.assert_not_small_order(&mut cs).is_err() == is_small_order); }; - let check_small_order_from_strs = |x, y| { - let (x, y) = ( - bls12_381::Scalar::from_str(x).unwrap(), - bls12_381::Scalar::from_str(y).unwrap(), + let check_small_order_from_strs = |u, v| { + let (u, v) = ( + bls12_381::Scalar::from_str(u).unwrap(), + bls12_381::Scalar::from_str(v).unwrap(), ); - let p = jubjub::AffinePoint::from_raw_unchecked(x, y); + let p = jubjub::AffinePoint::from_raw_unchecked(u, v); check_small_order_from_p(p.into(), true); }; @@ -1059,10 +1064,10 @@ mod test { .unwrap(); let largest_small_subgroup_order = jubjub::Fr::from_str("8").unwrap(); - let (zero_x, zero_y) = (bls12_381::Scalar::zero(), bls12_381::Scalar::one()); + let (zero_u, zero_v) = (bls12_381::Scalar::zero(), bls12_381::Scalar::one()); // generator for jubjub - let (x, y) = ( + let (u, v) = ( bls12_381::Scalar::from_str( "11076627216317271660298050606127911965867021807910416450833192264015104452986", ) @@ -1072,7 +1077,7 @@ mod test { ) .unwrap(), ); - let g = jubjub::AffinePoint::from_raw_unchecked(x, y).into(); + let g = jubjub::AffinePoint::from_raw_unchecked(u, v).into(); check_small_order_from_p(g, false); // generator for the prime subgroup @@ -1081,11 +1086,11 @@ mod test { let prime_subgroup_order_minus_1 = prime_subgroup_order - jubjub::Fr::one(); let should_not_be_zero = g_prime * prime_subgroup_order_minus_1; - assert_ne!(zero_x, should_not_be_zero.to_affine().get_u()); - assert_ne!(zero_y, should_not_be_zero.to_affine().get_v()); + assert_ne!(zero_u, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_v, should_not_be_zero.to_affine().get_v()); let should_be_zero = should_not_be_zero + g_prime; - assert_eq!(zero_x, should_be_zero.to_affine().get_u()); - assert_eq!(zero_y, should_be_zero.to_affine().get_v()); + assert_eq!(zero_u, should_be_zero.to_affine().get_u()); + assert_eq!(zero_v, should_be_zero.to_affine().get_v()); // generator for the small order subgroup let g_small = g * prime_subgroup_order_minus_1; @@ -1096,12 +1101,12 @@ mod test { let largest_small_subgroup_order_minus_1 = largest_small_subgroup_order - jubjub::Fr::one(); let should_not_be_zero = g_small * largest_small_subgroup_order_minus_1; - assert_ne!(zero_x, should_not_be_zero.to_affine().get_u()); - assert_ne!(zero_y, should_not_be_zero.to_affine().get_v()); + assert_ne!(zero_u, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_v, should_not_be_zero.to_affine().get_v()); let should_be_zero = should_not_be_zero + g_small; - assert_eq!(zero_x, should_be_zero.to_affine().get_u()); - assert_eq!(zero_y, should_be_zero.to_affine().get_v()); + assert_eq!(zero_u, should_be_zero.to_affine().get_u()); + assert_eq!(zero_v, should_be_zero.to_affine().get_v()); // take all the points from the script // assert should be different than multiplying by cofactor, which is the solution diff --git a/zcash_proofs/src/circuit/pedersen_hash.rs b/zcash_proofs/src/circuit/pedersen_hash.rs index 06e912c650..2ba02f9691 100644 --- a/zcash_proofs/src/circuit/pedersen_hash.rs +++ b/zcash_proofs/src/circuit/pedersen_hash.rs @@ -234,8 +234,8 @@ mod test { )) .to_affine(); - assert_eq!(res.get_x().get_value().unwrap(), expected.get_u()); - assert_eq!(res.get_y().get_value().unwrap(), expected.get_v()); + assert_eq!(res.get_u().get_value().unwrap(), expected.get_u()); + assert_eq!(res.get_v().get_value().unwrap(), expected.get_v()); // Test against the output of a different personalization let unexpected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( @@ -244,8 +244,8 @@ mod test { )) .to_affine(); - assert!(res.get_x().get_value().unwrap() != unexpected.get_u()); - assert!(res.get_y().get_value().unwrap() != unexpected.get_v()); + assert!(res.get_u().get_value().unwrap() != unexpected.get_u()); + assert!(res.get_v().get_value().unwrap() != unexpected.get_v()); } } } @@ -257,11 +257,11 @@ mod test { 0xbc, 0xe5, ]); - let expected_xs = [ + let expected_us = [ "28161926966428986673895580777285905189725480206811328272001879986576840909576", "39669831794597628158501766225645040955899576179071014703006420393381978263045", ]; - let expected_ys = [ + let expected_vs = [ "26869991781071974894722407757894142583682396277979904369818887810555917099932", "2112827187110048608327330788910224944044097981650120385961435904443901436107", ]; @@ -291,12 +291,12 @@ mod test { assert!(cs.is_satisfied()); assert_eq!( - res.get_x().get_value().unwrap(), - bls12_381::Scalar::from_str(expected_xs[length - 300]).unwrap() + res.get_u().get_value().unwrap(), + bls12_381::Scalar::from_str(expected_us[length - 300]).unwrap() ); assert_eq!( - res.get_y().get_value().unwrap(), - bls12_381::Scalar::from_str(expected_ys[length - 300]).unwrap() + res.get_v().get_value().unwrap(), + bls12_381::Scalar::from_str(expected_vs[length - 300]).unwrap() ); } } diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index b7d86cc34f..8715af5c0e 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -287,7 +287,7 @@ impl Circuit for Spend { // This is an injective encoding, as cur is a // point in the prime order subgroup. - let mut cur = cm.get_x().clone(); + let mut cur = cm.get_u().clone(); // Ascend the merkle tree authentication path for (i, e) in self.auth_path.into_iter().enumerate() { @@ -309,7 +309,7 @@ impl Circuit for Spend { num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?; // Swap the two if the current subtree is on the right - let (xl, xr) = num::AllocatedNum::conditionally_reverse( + let (ul, ur) = num::AllocatedNum::conditionally_reverse( cs.namespace(|| "conditional reversal of preimage"), &cur, &path_element, @@ -321,8 +321,8 @@ impl Circuit for Spend { // they will be unable to find an authentication path in the // tree with high probability. let mut preimage = vec![]; - preimage.extend(xl.to_bits_le(cs.namespace(|| "xl into bits"))?); - preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?); + preimage.extend(ul.to_bits_le(cs.namespace(|| "ul into bits"))?); + preimage.extend(ur.to_bits_le(cs.namespace(|| "ur into bits"))?); // Compute the new subtree value cur = pedersen_hash::pedersen_hash( @@ -330,7 +330,7 @@ impl Circuit for Spend { pedersen_hash::Personalization::MerkleTree(i), &preimage, )? - .get_x() + .get_u() .clone(); // Injective encoding } @@ -449,21 +449,21 @@ impl Circuit for Output { .as_ref() .map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine()); - // Witness the y-coordinate, encoded as little + // Witness the v-coordinate, encoded as little // endian bits (to match the representation) - let y_contents = boolean::field_into_boolean_vec_le( - cs.namespace(|| "pk_d bits of y"), + let v_contents = boolean::field_into_boolean_vec_le( + cs.namespace(|| "pk_d bits of v"), pk_d.map(|e| e.get_v()), )?; // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( - cs.namespace(|| "pk_d bit of x"), + cs.namespace(|| "pk_d bit of u"), pk_d.map(|e| e.get_u().is_odd()), )?); // Extend the note with pk_d representation - note_contents.extend(y_contents); + note_contents.extend(v_contents); note_contents.push(sign_bit); } @@ -499,11 +499,11 @@ impl Circuit for Output { cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?; } - // Only the x-coordinate of the output is revealed, + // Only the u-coordinate of the output is revealed, // since we know it is prime order, and we know that // the x-coordinate is an injective encoding for // prime-order elements. - cm.get_x().inputize(cs.namespace(|| "commitment"))?; + cm.get_u().inputize(cs.namespace(|| "commitment"))?; Ok(()) } @@ -636,18 +636,18 @@ fn test_input_circuit_with_bls12_381() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cmu); + assert_eq!(cs.get("randomization of note commitment/u3/num"), cmu); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u()); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); + assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v()); assert_eq!( - cs.get_input(3, "value commitment/commitment point/x/input variable"), + cs.get_input(3, "value commitment/commitment point/u/input variable"), expected_value_commitment.get_u() ); assert_eq!( - cs.get_input(4, "value commitment/commitment point/y/input variable"), + cs.get_input(4, "value commitment/commitment point/v/input variable"), expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); @@ -676,7 +676,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let tree_depth = 32; - let expected_commitment_xs = vec![ + let expected_commitment_us = vec![ "43821661663052659750276289184181083197337192946256245809816728673021647664276", "7220807656052227578299730541645543434083158611414003423211850718229633594616", "13239753550660714843257636471668037031928211668773449453628093339627668081697", @@ -689,7 +689,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "18269767207277008186871145355531741929166733260352590789136389380124992250945", ]; - let expected_commitment_ys = vec![ + let expected_commitment_vs = vec![ "27630722367128086497290371604583225252915685718989450292520883698391703910", "23310648738313092772044712773481584369462075017189681529702825235349449805260", "25709635353183537915646348052945798827495141780341329896098121888376871589480", @@ -745,11 +745,11 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); assert_eq!( expected_value_commitment.get_u(), - bls12_381::Scalar::from_str(&expected_commitment_xs[i as usize]).unwrap() + bls12_381::Scalar::from_str(&expected_commitment_us[i as usize]).unwrap() ); assert_eq!( expected_value_commitment.get_v(), - bls12_381::Scalar::from_str(&expected_commitment_ys[i as usize]).unwrap() + bls12_381::Scalar::from_str(&expected_commitment_vs[i as usize]).unwrap() ); let note = Note { value: value_commitment.value, @@ -818,18 +818,18 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cmu); + assert_eq!(cs.get("randomization of note commitment/u3/num"), cmu); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u()); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v()); + assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v()); assert_eq!( - cs.get_input(3, "value commitment/commitment point/x/input variable"), + cs.get_input(3, "value commitment/commitment point/u/input variable"), expected_value_commitment.get_u() ); assert_eq!( - cs.get_input(4, "value commitment/commitment point/y/input variable"), + cs.get_input(4, "value commitment/commitment point/v/input variable"), expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); @@ -924,19 +924,19 @@ fn test_output_circuit_with_bls12_381() { assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); assert_eq!( - cs.get_input(1, "value commitment/commitment point/x/input variable"), + cs.get_input(1, "value commitment/commitment point/u/input variable"), expected_value_commitment.get_u() ); assert_eq!( - cs.get_input(2, "value commitment/commitment point/y/input variable"), + cs.get_input(2, "value commitment/commitment point/v/input variable"), expected_value_commitment.get_v() ); assert_eq!( - cs.get_input(3, "epk/x/input variable"), + cs.get_input(3, "epk/u/input variable"), expected_epk.get_u() ); assert_eq!( - cs.get_input(4, "epk/y/input variable"), + cs.get_input(4, "epk/v/input variable"), expected_epk.get_v() ); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cmu); diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 8821a57e67..e41e5ddcfd 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -115,15 +115,15 @@ impl SaplingProvingContext { let mut public_input = [bls12_381::Scalar::zero(); 7]; { let affine = rk.0.to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[0] = x; - public_input[1] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { let affine = jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[2] = x; - public_input[3] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } public_input[4] = anchor; diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index d30ec9b215..fcbb86c596 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -63,15 +63,15 @@ impl SaplingVerificationContext { let mut public_input = [bls12_381::Scalar::zero(); 7]; { let affine = rk.0.to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[0] = x; - public_input[1] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { let affine = cv.to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[2] = x; - public_input[3] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } public_input[4] = anchor; @@ -111,15 +111,15 @@ impl SaplingVerificationContext { let mut public_input = [bls12_381::Scalar::zero(); 5]; { let affine = cv.to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[0] = x; - public_input[1] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { let affine = epk.to_affine(); - let (x, y) = (affine.get_u(), affine.get_v()); - public_input[2] = x; - public_input[3] = y; + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } public_input[4] = cmu; From 74abb88dcec52e742ec62c4b186270cd335f1956 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sat, 22 Aug 2020 00:56:17 +0100 Subject: [PATCH 177/210] Fix some comments. Signed-off-by: Daira Hopwood --- zcash_proofs/src/circuit/ecc.rs | 2 +- zcash_proofs/src/circuit/sapling.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 933764366a..61667e1f39 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -539,7 +539,7 @@ impl MontgomeryPoint { } /// Performs an affine point addition, not defined for - /// coincident points. + /// points with the same x-coordinate. pub fn add(&self, mut cs: CS, other: &Self) -> Result where CS: ConstraintSystem, diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 8715af5c0e..93e3e97e9a 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -153,9 +153,9 @@ impl Circuit for Spend { )?; // NB: We don't ensure that the bit representation of nsk - // is "in the field" (Fs) because it's not used except to - // demonstrate the prover knows it. If they know a - // congruency then that's equivalent. + // is "in the field" (jubjub::Fr) because it's not used + // except to demonstrate the prover knows it. If they know + // a congruency then that's equivalent. // Compute nk = [nsk] ProvingPublicKey nk = ecc::fixed_base_multiplication( @@ -501,8 +501,8 @@ impl Circuit for Output { // Only the u-coordinate of the output is revealed, // since we know it is prime order, and we know that - // the x-coordinate is an injective encoding for - // prime-order elements. + // the u-coordinate is an injective encoding for + // elements in the prime-order subgroup. cm.get_u().inputize(cs.namespace(|| "commitment"))?; Ok(()) From 56da51f3ce79fa4e6144af5f986d3bb9e0838590 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 02:03:05 +0100 Subject: [PATCH 178/210] Squashed 'jubjub/' changes from ec85333e..38d38af3 38d38af3 Merge pull request #32 from kevaundray/patch-1 af5598da Merge pull request #33 from ZcashFoundation/scalar 109ec40d Add public Scalar type alias for Fr 8e9c5fe6 typo in Fr.rs 8e9337ee Merge pull request #30 from rex4539/typos 5f4374c8 Fix typo git-subtree-dir: jubjub git-subtree-split: 38d38af3b792d2c55d815d214a7cd157dc8f71ad --- src/fr.rs | 4 ++-- src/lib.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/fr.rs b/src/fr.rs index 4495e3bd7f..b8412c779c 100644 --- a/src/fr.rs +++ b/src/fr.rs @@ -139,7 +139,7 @@ const R2: Fr = Fr([ 0x04f6547b8d127688, ]); -/// R^2 = 2^768 mod r +/// R^3 = 2^768 mod r const R3: Fr = Fr([ 0xe0d6c6563d830544, 0x323e3883598d0f85, @@ -241,7 +241,7 @@ impl Fr { // // and computing their sum in the field. It remains to see that arbitrary 256-bit // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by + // reduction works so long as the product is less than R=2^256 multiplied by // the modulus. This holds because for any `c` smaller than the modulus, we have // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the // reduction always works so long as `c` is in the field; in this case it is either the diff --git a/src/lib.rs b/src/lib.rs index 8419487964..3de1a2d96d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ mod fr; pub use bls12_381::Scalar as Fq; pub use fr::Fr; +/// A better name than Fr. +pub type Scalar = Fr; + const FR_MODULUS_BYTES: [u8; 32] = [ 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, From 9de623e4fa37804e690757e0498eaa43864b9d1b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 23 Aug 2020 06:27:54 +0100 Subject: [PATCH 179/210] jubjub: Fix intra-doc link in SubgroupPoint::from_raw_unchecked --- jubjub/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs index cb4699bdab..d55dd6c9b1 100644 --- a/jubjub/src/lib.rs +++ b/jubjub/src/lib.rs @@ -1030,6 +1030,8 @@ impl SubgroupPoint { /// /// This should only be used for hard-coding constants (e.g. fixed generators); in all /// other cases, use [`SubgroupPoint::from_bytes`] instead. + /// + /// [`SubgroupPoint::from_bytes`]: SubgroupPoint#impl-GroupEncoding pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) } From c3d36c94bf4e956af7707771dc93eede6999981c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 23 Aug 2020 06:47:06 +0100 Subject: [PATCH 180/210] bls12_381: Add feature flags to docs.rs documentation --- bls12_381/src/g1.rs | 2 ++ bls12_381/src/g2.rs | 2 ++ bls12_381/src/lib.rs | 1 + bls12_381/src/pairings.rs | 6 ++++++ 4 files changed, 11 insertions(+) diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs index 7328c3d3cc..d30f882401 100644 --- a/bls12_381/src/g1.rs +++ b/bls12_381/src/g1.rs @@ -20,6 +20,7 @@ use crate::Scalar; /// /// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an /// "unchecked" API was misused. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] #[derive(Copy, Clone, Debug)] pub struct G1Affine { pub(crate) x: Fp, @@ -414,6 +415,7 @@ impl G1Affine { } /// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] #[derive(Copy, Clone, Debug)] pub struct G1Projective { x: Fp, diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs index ce59b89d46..b0e7a87638 100644 --- a/bls12_381/src/g2.rs +++ b/bls12_381/src/g2.rs @@ -21,6 +21,7 @@ use crate::Scalar; /// /// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an /// "unchecked" API was misused. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] #[derive(Copy, Clone, Debug)] pub struct G2Affine { pub(crate) x: Fp2, @@ -486,6 +487,7 @@ impl G2Affine { } /// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. +#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] #[derive(Copy, Clone, Debug)] pub struct G2Projective { pub(crate) x: Fp2, diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs index 5e8a7db290..7f16f1b472 100644 --- a/bls12_381/src/lib.rs +++ b/bls12_381/src/lib.rs @@ -9,6 +9,7 @@ //! * All operations are constant time unless explicitly noted. #![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] #![deny(missing_debug_implementations)] diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs index 440d3038fb..6e9cd4b89c 100644 --- a/bls12_381/src/pairings.rs +++ b/bls12_381/src/pairings.rs @@ -19,6 +19,7 @@ use alloc::vec::Vec; /// Represents results of a Miller loop, one of the most expensive portions /// of the pairing function. `MillerLoopResult`s cannot be compared with each /// other until `.final_exponentiation()` is called, which is also expensive. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] #[derive(Copy, Clone, Debug)] pub struct MillerLoopResult(pub(crate) Fp12); @@ -180,6 +181,7 @@ impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopRes /// /// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to /// keep code and abstractions consistent. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] #[derive(Copy, Clone, Debug, Default)] pub struct Gt(pub(crate) Fp12); @@ -449,6 +451,7 @@ impl Group for Gt { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))] #[derive(Clone, Debug)] /// This structure contains cached computations pertaining to a $\mathbb{G}_2$ /// element as part of the pairing function (specifically, the Miller loop) and @@ -509,6 +512,7 @@ impl From for G2Prepared { } #[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))] /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ /// @@ -565,6 +569,7 @@ pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult } /// Invoke the pairing function without the use of precomputation and other optimizations. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { struct Adder { cur: G2Projective, @@ -749,6 +754,7 @@ impl PairingCurveAffine for G2Affine { } /// A [`pairing::Engine`] for BLS12-381 pairing operations. +#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] #[derive(Clone, Debug)] pub struct Bls12; From a3cb89dff9d806ebb03a0ba03e6cdf430374519e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 23 Aug 2020 06:56:03 +0100 Subject: [PATCH 181/210] zcash_primitives: Add feature flags to docs.rs documentation --- zcash_primitives/Cargo.toml | 3 +++ zcash_primitives/src/lib.rs | 1 + zcash_primitives/src/transaction/builder.rs | 1 + zcash_primitives/src/transaction/components.rs | 1 + 4 files changed, 6 insertions(+) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 0aedae3609..bef7f4edc1 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -11,6 +11,9 @@ readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" +[package.metadata.docs.rs] +all-features = true + [dependencies] aes = "0.5" blake2b_simd = "0.5" diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index d20a4ace9d..b4908b578d 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -3,6 +3,7 @@ //! `zcash_primitives` is a library that provides the core structs and functions necessary //! for working with Zcash. +#![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index d6c2ea5965..302656edfd 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -412,6 +412,7 @@ impl Builder { /// Adds a transparent coin to be spent in this transaction. #[cfg(feature = "transparent-inputs")] + #[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))] pub fn add_transparent_input( &mut self, sk: secp256k1::SecretKey, diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 60a7dc8949..0929eda51f 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -60,6 +60,7 @@ pub struct TxIn { impl TxIn { #[cfg(feature = "transparent-inputs")] + #[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))] pub fn new(prevout: OutPoint) -> Self { TxIn { prevout, From cda56ef862e5292417f5897900e11588cb1e1004 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 23 Aug 2020 10:54:32 +0100 Subject: [PATCH 182/210] zcash_proofs: Add feature flags to docs.rs documentation --- zcash_proofs/Cargo.toml | 3 +++ zcash_proofs/src/lib.rs | 7 +++++++ zcash_proofs/src/prover.rs | 3 +++ 3 files changed, 13 insertions(+) diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 8a917e2f85..74567b4600 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -11,6 +11,9 @@ readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" +[package.metadata.docs.rs] +all-features = true + [dependencies] bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index ecbb34c0bf..6d0476c578 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -3,6 +3,7 @@ //! `zcash_proofs` contains the zk-SNARK circuits used by Zcash, and the APIs for creating //! and verifying proofs. +#![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] @@ -24,6 +25,10 @@ pub mod sapling; pub mod sprout; #[cfg(any(feature = "local-prover", feature = "bundled-prover"))] +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "local-prover", feature = "bundled-prover"))) +)] pub mod prover; // Circuit names @@ -42,6 +47,7 @@ const DOWNLOAD_URL: &str = "https://download.z.cash/downloads"; /// Returns the default folder that the Zcash proving parameters are located in. #[cfg(feature = "directories")] +#[cfg_attr(docsrs, doc(cfg(feature = "directories")))] pub fn default_params_folder() -> Option { BaseDirs::new().map(|base_dirs| { if cfg!(any(windows, target_os = "macos")) { @@ -56,6 +62,7 @@ pub fn default_params_folder() -> Option { /// /// This mirrors the behaviour of the `fetch-params.sh` script from `zcashd`. #[cfg(feature = "download-params")] +#[cfg_attr(docsrs, doc(cfg(feature = "download-params")))] pub fn download_parameters() -> Result<(), minreq::Error> { // Ensure that the default Zcash parameters location exists. let params_dir = default_params_folder().ok_or(io::Error::new( diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index 5373bc3f9f..f84b22279a 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -49,6 +49,7 @@ impl LocalTxProver { /// This function will panic if the paths do not point to valid parameter files with /// the expected hashes. #[cfg(feature = "local-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "local-prover")))] pub fn new(spend_path: &Path, output_path: &Path) -> Self { let (spend_params, spend_vk, output_params, _, _) = load_parameters(spend_path, output_path, None); @@ -81,6 +82,7 @@ impl LocalTxProver { /// This function will panic if the parameters in the default local location do not /// have the expected hashes. #[cfg(feature = "local-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "local-prover")))] pub fn with_default_location() -> Option { let params_dir = default_params_folder()?; let (spend_path, output_path) = if params_dir.exists() { @@ -103,6 +105,7 @@ impl LocalTxProver { /// This requires the `bundled-prover` feature, which will increase the binary size by /// around 50 MiB. #[cfg(feature = "bundled-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "bundled-prover")))] pub fn bundled() -> Self { let (spend_buf, output_buf) = wagyu_zcash_parameters::load_sapling_parameters(); let (spend_params, spend_vk, output_params, _, _) = From 55fa366730bb71429dd6e11c913a06e3ab42fe66 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:31:01 +0100 Subject: [PATCH 183/210] ff 0.7.0 --- bellman/Cargo.toml | 2 +- bls12_381/Cargo.toml | 2 +- ff/Cargo.toml | 9 ++++++--- ff/README.md | 4 ++-- ff/ff_derive/Cargo.toml | 2 +- group/Cargo.toml | 2 +- jubjub/Cargo.toml | 2 +- pairing/Cargo.toml | 2 +- zcash_client_backend/Cargo.toml | 2 +- zcash_client_sqlite/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 12 files changed, 18 insertions(+), 15 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 45fd968ff7..bfddabf3c2 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] bit-vec = "0.6" blake2s_simd = "0.5" -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } futures = "0.1" futures-cpupool = { version = "0.1", optional = true } group = { version = "0.6", path = "../group" } diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 1799294f29..8fa1850c9b 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -26,7 +26,7 @@ default-features = false [dependencies.ff] path = "../ff" -version = "0.6" +version = "0.7" default-features = false [dependencies.group] diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 01cc6c6513..e081e84c12 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "ff" -version = "0.6.0" -authors = ["Sean Bowe "] +version = "0.7.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", +] description = "Library for building and interfacing with finite fields" readme = "README.md" documentation = "https://docs.rs/ff/" @@ -12,7 +15,7 @@ edition = "2018" [dependencies] byteorder = { version = "1", default-features = false } -ff_derive = { version = "0.6", path = "ff_derive", optional = true } +ff_derive = { version = "0.7", path = "ff_derive", optional = true } rand_core = { version = "0.5", default-features = false } subtle = { version = "2.2.1", default-features = false, features = ["i128"] } diff --git a/ff/README.md b/ff/README.md index 57ef693f35..4fbe19028b 100644 --- a/ff/README.md +++ b/ff/README.md @@ -12,7 +12,7 @@ Add the `ff` crate to your `Cargo.toml`: ```toml [dependencies] -ff = "0.5" +ff = "0.7" ``` The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. @@ -29,7 +29,7 @@ First, enable the `derive` crate feature: ```toml [dependencies] -ff = { version = "0.4", features = ["derive"] } +ff = { version = "0.7", features = ["derive"] } ``` And then use the macro like so: diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml index 89a45b315e..e1e26bc563 100644 --- a/ff/ff_derive/Cargo.toml +++ b/ff/ff_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ff_derive" -version = "0.6.0" +version = "0.7.0" authors = [ "Sean Bowe ", "Jack Grigg ", diff --git a/group/Cargo.toml b/group/Cargo.toml index ec5dff86a6..40b851eb64 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -16,7 +16,7 @@ edition = "2018" [dependencies] byteorder = { version = "1", default-features = false } -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } rand = "0.7" rand_xorshift = "0.2" subtle = { version = "2.2.1", default-features = false } diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index 5955e6cb73..f7a5c59048 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -24,7 +24,7 @@ default-features = false [dependencies.ff] path = "../ff" -version = "0.6" +version = "0.7" default-features = false [dependencies.group] diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml index d24e2d0ed0..4686ca2e65 100644 --- a/pairing/Cargo.toml +++ b/pairing/Cargo.toml @@ -18,7 +18,7 @@ edition ="2018" [dependencies] byteorder = "1" -ff = { version = "0.6", path = "../ff", features = ["derive"] } +ff = { version = "0.7", path = "../ff", features = ["derive"] } group = { version = "0.6", path = "../group" } rand_core = "0.5" subtle = "2.2.1" diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 8ceeb6e53b..942aabe5ca 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" bech32 = "0.7" bls12_381 = { version = "0.1", path = "../bls12_381" } bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } group = { version = "0.6", path = "../group" } hex = "0.4" jubjub = { version = "0.3", path = "../jubjub" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 61467a426a..87e7bacca1 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" [dependencies] bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } group = { version = "0.6", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } pairing = { version = "0.16", path = "../pairing" } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index bef7f4edc1..f8b03933ac 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -22,7 +22,7 @@ bls12_381 = { version = "0.1", path = "../bls12_381" } byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } fpe = "0.3" group = { version = "0.6", path = "../group" } hex = "0.4" diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 74567b4600..7d851681bb 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -20,7 +20,7 @@ blake2b_simd = "0.5" bls12_381 = { version = "0.1", path = "../bls12_381" } byteorder = "1" directories = { version = "3", optional = true } -ff = { version = "0.6", path = "../ff" } +ff = { version = "0.7", path = "../ff" } group = { version = "0.6", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" From ee3e8fbfbb4af6c249f026f6271935aa52301a30 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:33:31 +0100 Subject: [PATCH 184/210] group 0.7.0 --- bellman/Cargo.toml | 2 +- bls12_381/Cargo.toml | 2 +- group/Cargo.toml | 2 +- jubjub/Cargo.toml | 2 +- pairing/Cargo.toml | 2 +- zcash_client_backend/Cargo.toml | 2 +- zcash_client_sqlite/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index bfddabf3c2..79843b39a2 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -15,7 +15,7 @@ blake2s_simd = "0.5" ff = { version = "0.7", path = "../ff" } futures = "0.1" futures-cpupool = { version = "0.1", optional = true } -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } num_cpus = { version = "1", optional = true } crossbeam = { version = "0.7", optional = true } pairing = { version = "0.16", path = "../pairing", optional = true } diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 8fa1850c9b..92e65e2835 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -31,7 +31,7 @@ default-features = false [dependencies.group] path = "../group" -version = "0.6" +version = "0.7" default-features = false optional = true diff --git a/group/Cargo.toml b/group/Cargo.toml index 40b851eb64..57ad76f81d 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "group" -version = "0.6.0" +version = "0.7.0" authors = [ "Sean Bowe ", "Jack Grigg ", diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index f7a5c59048..5fdd35ef04 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -29,7 +29,7 @@ default-features = false [dependencies.group] path = "../group" -version = "0.6" +version = "0.7" default-features = false [dependencies.rand_core] diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml index 4686ca2e65..2805c0d605 100644 --- a/pairing/Cargo.toml +++ b/pairing/Cargo.toml @@ -19,7 +19,7 @@ edition ="2018" [dependencies] byteorder = "1" ff = { version = "0.7", path = "../ff", features = ["derive"] } -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } rand_core = "0.5" subtle = "2.2.1" diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 942aabe5ca..960da2f045 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -16,7 +16,7 @@ bech32 = "0.7" bls12_381 = { version = "0.1", path = "../bls12_381" } bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } hex = "0.4" jubjub = { version = "0.3", path = "../jubjub" } pairing = { version = "0.16", path = "../pairing" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 87e7bacca1..bdd79bc6a2 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } pairing = { version = "0.16", path = "../pairing" } protobuf = "2" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index f8b03933ac..6206a3902c 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -24,7 +24,7 @@ crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } ff = { version = "0.7", path = "../ff" } fpe = "0.3" -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } hex = "0.4" jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 7d851681bb..2b7bac2d3d 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -21,7 +21,7 @@ bls12_381 = { version = "0.1", path = "../bls12_381" } byteorder = "1" directories = { version = "3", optional = true } ff = { version = "0.7", path = "../ff" } -group = { version = "0.6", path = "../group" } +group = { version = "0.7", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } From bdb42bcbc8f2a4e8d9a8b918d9085574552e98ef Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:40:10 +0100 Subject: [PATCH 185/210] pairing 0.17.0 --- bellman/Cargo.toml | 2 +- bls12_381/Cargo.toml | 2 +- pairing/Cargo.toml | 2 +- pairing/README.md | 11 +++-------- zcash_client_backend/Cargo.toml | 1 - zcash_client_sqlite/Cargo.toml | 1 - zcash_primitives/Cargo.toml | 1 - zcash_primitives/src/note_encryption.rs | 1 - zcash_proofs/Cargo.toml | 1 - 9 files changed, 6 insertions(+), 16 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 79843b39a2..f6abb911f1 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -18,7 +18,7 @@ futures-cpupool = { version = "0.1", optional = true } group = { version = "0.7", path = "../group" } num_cpus = { version = "1", optional = true } crossbeam = { version = "0.7", optional = true } -pairing = { version = "0.16", path = "../pairing", optional = true } +pairing = { version = "0.17", path = "../pairing", optional = true } rand_core = "0.5" byteorder = "1" subtle = "2.2.1" diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 92e65e2835..585713424b 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -37,7 +37,7 @@ optional = true [dependencies.pairing] path = "../pairing" -version = "0.16" +version = "0.17" optional = true [dependencies.rand_core] diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml index 2805c0d605..5993a58874 100644 --- a/pairing/Cargo.toml +++ b/pairing/Cargo.toml @@ -2,7 +2,7 @@ name = "pairing" # Remember to change version string in README.md. -version = "0.16.0" +version = "0.17.0" authors = [ "Sean Bowe ", "Jack Grigg ", diff --git a/pairing/README.md b/pairing/README.md index 47a25dc686..e46fc58c3f 100644 --- a/pairing/README.md +++ b/pairing/README.md @@ -2,15 +2,10 @@ `pairing` is a crate for using pairing-friendly elliptic curves. -Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) -construction is implemented. +`pairing` provides basic traits for pairing-friendly elliptic curve constructions. +Specific curves are implemented in separate crates: -## Roadmap - -`pairing` is being refactored into a generic library for working with -pairing-friendly curves. After the refactor, `pairing` will provide basic traits -for pairing-friendly elliptic curve constructions, while specific curves will be -in separate crates. +- [`bls12_381`](https://crates.io/crates/bls12_381) - the BLS12-381 curve. ## [Documentation](https://docs.rs/pairing/) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 960da2f045..3163d83e8e 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -19,7 +19,6 @@ ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } hex = "0.4" jubjub = { version = "0.3", path = "../jubjub" } -pairing = { version = "0.16", path = "../pairing" } protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index bdd79bc6a2..0f1a6a84bf 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -17,7 +17,6 @@ bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } -pairing = { version = "0.16", path = "../pairing" } protobuf = "2" rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 6206a3902c..6973ccae34 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -29,7 +29,6 @@ hex = "0.4" jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" log = "0.4" -pairing = { version = "0.16", path = "../pairing" } rand = "0.7" rand_core = "0.5.1" ripemd160 = { version = "0.9", optional = true } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 6e120fe84a..292724d1f9 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -185,7 +185,6 @@ pub fn prf_ock( /// /// ``` /// extern crate ff; -/// extern crate pairing; /// extern crate rand_core; /// extern crate zcash_primitives; /// diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 2b7bac2d3d..8f7ee8dda8 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -25,7 +25,6 @@ group = { version = "0.7", path = "../group" } jubjub = { version = "0.3", path = "../jubjub" } lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } -pairing = { version = "0.16", path = "../pairing" } rand_core = "0.5.1" wagyu-zcash-parameters = { version = "0.2", optional = true } zcash_primitives = { version = "0.2", path = "../zcash_primitives" } From 5f99c52143c07857d22978622880fdfca91de435 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:46:27 +0100 Subject: [PATCH 186/210] bls12_381 0.2.0 --- bellman/Cargo.toml | 2 +- bls12_381/Cargo.toml | 7 +++++-- bls12_381/RELEASES.md | 32 ++++++++++++++++++++++++++++++++ jubjub/Cargo.toml | 2 +- zcash_client_backend/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 7 files changed, 42 insertions(+), 7 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index f6abb911f1..81ec8cdd31 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -24,7 +24,7 @@ byteorder = "1" subtle = "2.2.1" [dev-dependencies] -bls12_381 = { version = "0.1", path = "../bls12_381" } +bls12_381 = { version = "0.2", path = "../bls12_381" } hex-literal = "0.2" rand = "0.7" rand_xorshift = "0.2" diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml index 585713424b..4411401425 100644 --- a/bls12_381/Cargo.toml +++ b/bls12_381/Cargo.toml @@ -1,12 +1,15 @@ [package] -authors = ["Sean Bowe "] +authors = [ + "Sean Bowe ", + "Jack Grigg ", +] description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction" documentation = "https://docs.rs/bls12_381/" homepage = "https://github.com/zkcrypto/bls12_381" license = "MIT/Apache-2.0" name = "bls12_381" repository = "https://github.com/zkcrypto/bls12_381" -version = "0.1.1" +version = "0.2.0" edition = "2018" [package.metadata.docs.rs] diff --git a/bls12_381/RELEASES.md b/bls12_381/RELEASES.md index 85fcd4ae5a..5f314c4379 100644 --- a/bls12_381/RELEASES.md +++ b/bls12_381/RELEASES.md @@ -1,3 +1,35 @@ +# 0.2.0 + +This release adds implementations of the `ff`, `group`, and `pairing` traits (with the +latter two being gated by the `groups` and `pairings` feature flags respectively). +Additional trait implementations (for standard traits) have been added where the `ff`, +`group`, and `pairing` trait bounds require them. + +## Added +* `bls12_381::Bls12`, a `pairing::Engine` for BLS12-381 pairing operations. It implements + the following traits: + * `pairing::{Engine, MultiMillerLoop}` +* New trait implementations for `bls12_381::G1Projective`: + * `group::{Curve, Group, GroupEncoding, WnafGroup}` + * `group::prime::{PrimeCurve, PrimeGroup}` +* New trait implementations for `bls12_381::G1Affine`: + * `group::{GroupEncoding, UncompressedEncoding}` + * `group::prime::PrimeCurveAffine` + * `pairing::PairingCurveAffine` +* New trait implementations for `bls12_381::G2Projective`: + * `group::{Curve, Group, GroupEncoding, WnafGroup}` + * `group::prime::{PrimeCurve, PrimeGroup}` +* New trait implementations for `bls12_381::G2Affine`: + * `group::{GroupEncoding, UncompressedEncoding}` + * `group::prime::PrimeCurveAffine` + * `pairing::PairingCurveAffine` +* New trait implementations for `bls12_381::Gt`: + * `group::Group` +* New trait implementations for `bls12_381::MillerLoopResult`: + * `pairing::MillerLoopResult` +* New trait implementations for `bls12_381::Scalar`: + * `ff::{Field, PrimeField}` + # 0.1.1 Added `clear_cofactor` methods to `G1Projective` and `G2Projective`. If the crate feature `endo` diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index 5fdd35ef04..af07bfd9a7 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" [dependencies.bls12_381] path = "../bls12_381" -version = "0.1" +version = "0.2" default-features = false [dependencies.byteorder] diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 3163d83e8e..c5de72508e 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [dependencies] bech32 = "0.7" -bls12_381 = { version = "0.1", path = "../bls12_381" } +bls12_381 = { version = "0.2", path = "../bls12_381" } bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 6973ccae34..c18a95628e 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -18,7 +18,7 @@ all-features = true aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" -bls12_381 = { version = "0.1", path = "../bls12_381" } +bls12_381 = { version = "0.2", path = "../bls12_381" } byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 8f7ee8dda8..ae297dbbce 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -17,7 +17,7 @@ all-features = true [dependencies] bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" -bls12_381 = { version = "0.1", path = "../bls12_381" } +bls12_381 = { version = "0.2", path = "../bls12_381" } byteorder = "1" directories = { version = "3", optional = true } ff = { version = "0.7", path = "../ff" } From d112add8a3fa9bce4a130777be682259d895062c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:48:19 +0100 Subject: [PATCH 187/210] jubjub 0.4.0 --- jubjub/Cargo.toml | 6 +----- jubjub/RELEASES.md | 29 +++++++++++++++++++++++++++++ zcash_client_backend/Cargo.toml | 2 +- zcash_client_sqlite/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml index af07bfd9a7..3f9485ca95 100644 --- a/jubjub/Cargo.toml +++ b/jubjub/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/zkcrypto/jubjub" license = "MIT/Apache-2.0" name = "jubjub" repository = "https://github.com/zkcrypto/jubjub" -version = "0.3.0" +version = "0.4.0" edition = "2018" [dependencies.bls12_381] @@ -43,10 +43,6 @@ default-features = false [dev-dependencies] criterion = "0.3" -[dev-dependencies.rand_core] -version = "0.5" -default-features = false - [dev-dependencies.rand_xorshift] version = "0.2" default-features = false diff --git a/jubjub/RELEASES.md b/jubjub/RELEASES.md index 45db61c1ff..b9a1e9efff 100644 --- a/jubjub/RELEASES.md +++ b/jubjub/RELEASES.md @@ -1,3 +1,32 @@ +# 0.4.0 + +This release adds implementations of the `ff` and `group` traits. Additional trait +implementations (for standard traits) have been added where the `ff` and `group` trait +bounds require them. + +## Added +* `jubjub::SubgroupPoint`, which represents an element of Jubjub's prime-order subgroup. + It implements the following traits: + * `group::{Group, GroupEncoding}` + * `group::prime::PrimeGroup` +* New trait implementations for `jubjub::ExtendedPoint`: + * `group::{Curve, Group, GroupEncoding, WnafGroup}` + * `group::cofactor::{CofactorCurve, CofactorGroup}` +* New trait implementations for `jubjub::AffinePoint`: + * `group::GroupEncoding` + * `group::cofactor::CofactorCurveAffine` +* New trait implementations for `jubjub::Fr`: + * `ff::{Field, PrimeField}` +* `jubjub::AffinePoint::is_identity` +* `jubjub::AffinePoint::to_extended` +* `jubjub::Scalar`, as an alias for `jubjub::Fr`. + +## Changed +* We've migrated to `bls12_381 0.2`. +* `rand_core` is now a regular dependency. +* We depend on the `byteorder` crate again, as it is part of the `ff::PrimeField` trait. +* The benchmarks are now implemented using `criterion`. + # 0.3.0 This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export. diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index c5de72508e..620f29eac6 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -18,7 +18,7 @@ bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } hex = "0.4" -jubjub = { version = "0.3", path = "../jubjub" } +jubjub = { version = "0.4", path = "../jubjub" } protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" zcash_primitives = { version = "0.2", path = "../zcash_primitives" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 0f1a6a84bf..efdfac8592 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -16,7 +16,7 @@ bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } -jubjub = { version = "0.3", path = "../jubjub" } +jubjub = { version = "0.4", path = "../jubjub" } protobuf = "2" rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index c18a95628e..e1f6692ac6 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -26,7 +26,7 @@ ff = { version = "0.7", path = "../ff" } fpe = "0.3" group = { version = "0.7", path = "../group" } hex = "0.4" -jubjub = { version = "0.3", path = "../jubjub" } +jubjub = { version = "0.4", path = "../jubjub" } lazy_static = "1" log = "0.4" rand = "0.7" diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index ae297dbbce..472d8d1532 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -22,7 +22,7 @@ byteorder = "1" directories = { version = "3", optional = true } ff = { version = "0.7", path = "../ff" } group = { version = "0.7", path = "../group" } -jubjub = { version = "0.3", path = "../jubjub" } +jubjub = { version = "0.4", path = "../jubjub" } lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } rand_core = "0.5.1" From 1124eb3ca9ad70df5e6dd201073c7a05da1e8e61 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 11:57:27 +0100 Subject: [PATCH 188/210] bellman 0.7.0 --- bellman/Cargo.toml | 2 +- bellman/README.md | 13 ++++++++----- zcash_proofs/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 81ec8cdd31..a6f02b3a40 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://github.com/ebfull/bellman" license = "MIT/Apache-2.0" name = "bellman" repository = "https://github.com/ebfull/bellman" -version = "0.6.0" +version = "0.7.0" edition = "2018" [dependencies] diff --git a/bellman/README.md b/bellman/README.md index d64dd9c1e4..850f2e3e67 100644 --- a/bellman/README.md +++ b/bellman/README.md @@ -4,13 +4,16 @@ and primitive structures, as well as basic gadget implementations such as booleans and number abstractions. +`bellman` uses the `ff` and `group` crates to build circuits generically over a +scalar field type, which is used as the "word" of a circuit. Arithmetic +operations modulo the scalar field's prime are efficient, while other operations +(such as boolean logic) are implemented using these words. + ## Roadmap -`bellman` is being refactored into a generic proving library. Currently it is -pairing-specific, and different types of proving systems need to be implemented -as sub-modules. After the refactor, `bellman` will be generic using the `ff` and -`group` crates, while specific proving systems will be separate crates that pull -in the dependencies they require. +Currently `bellman` bundles an implementation of the Groth16 proving system. +This will be moved into a separate crate in the future, and `bellman` will +contain any utilities that make implementing proving systems easier. ## License diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 472d8d1532..6e4f7773d0 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" all-features = true [dependencies] -bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } +bellman = { version = "0.7", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" bls12_381 = { version = "0.2", path = "../bls12_381" } byteorder = "1" From e25a7dacdf86cd873a56f0b89c20d62b5fec50d1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 12:06:21 +0100 Subject: [PATCH 189/210] zcash_primitives 0.3.0 --- zcash_client_backend/Cargo.toml | 2 +- zcash_client_sqlite/Cargo.toml | 2 +- zcash_primitives/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 620f29eac6..49b4b8bfa2 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -21,7 +21,7 @@ hex = "0.4" jubjub = { version = "0.4", path = "../jubjub" } protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" -zcash_primitives = { version = "0.2", path = "../zcash_primitives" } +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [build-dependencies] protobuf-codegen-pure = "2.14" diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index efdfac8592..e1ed3357d7 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -22,7 +22,7 @@ rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } time = "0.1" zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } -zcash_primitives = { version = "0.2", path = "../zcash_primitives" } +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [dev-dependencies] rand_core = "0.5.1" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index e1f6692ac6..c60d94f326 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_primitives" description = "Rust implementations of the Zcash primitives" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 6e4f7773d0..ec3efe9140 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -27,7 +27,7 @@ lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } rand_core = "0.5.1" wagyu-zcash-parameters = { version = "0.2", optional = true } -zcash_primitives = { version = "0.2", path = "../zcash_primitives" } +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [dev-dependencies] rand_xorshift = "0.2" From 9ce0994f0b4f68eb73dfdc57d95178af6a54263f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 12:07:02 +0100 Subject: [PATCH 190/210] zcash_proofs 0.3.0 --- zcash_client_sqlite/Cargo.toml | 2 +- zcash_proofs/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index e1ed3357d7..7af12a13b4 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -27,7 +27,7 @@ zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [dev-dependencies] rand_core = "0.5.1" tempfile = "3" -zcash_proofs = { version = "0.2", path = "../zcash_proofs" } +zcash_proofs = { version = "0.3", path = "../zcash_proofs" } [features] mainnet = [] diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index ec3efe9140..6dc1bbdd90 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_proofs" description = "Zcash zk-SNARK circuits and proving APIs" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] From 0a3752c0880199528ddb20a8423b529b01b8c254 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 12:09:27 +0100 Subject: [PATCH 191/210] zcash_client_backend 0.3.0 --- zcash_client_backend/Cargo.toml | 2 +- zcash_client_sqlite/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 49b4b8bfa2..140d6d57b7 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_client_backend" description = "APIs for creating shielded Zcash light clients" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 7af12a13b4..e6a0395796 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -21,7 +21,7 @@ protobuf = "2" rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } time = "0.1" -zcash_client_backend = { version = "0.2", path = "../zcash_client_backend" } +zcash_client_backend = { version = "0.3", path = "../zcash_client_backend" } zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [dev-dependencies] From 3cd8f64240609f517022ac0382fa7d4fc979f773 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 22 Aug 2020 12:14:14 +0100 Subject: [PATCH 192/210] zcash_client_sqlite 0.1.0 --- zcash_client_sqlite/Cargo.toml | 2 +- zcash_client_sqlite/README.md | 37 +++++----------------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index e6a0395796..32b5db3ca7 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_client_sqlite" description = "An SQLite-based Zcash light client" -version = "0.0.0" +version = "0.1.0" authors = [ "Jack Grigg ", ] diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md index d73e3fe3b4..af077e8d59 100644 --- a/zcash_client_sqlite/README.md +++ b/zcash_client_sqlite/README.md @@ -1,39 +1,12 @@ # Security Disclaimer -#### :warning: WARNING: This is an *early preview* - ----- - -In the spirit of transparency, we provide this as a window into what we are actively -developing. This is an alpha build, not yet intended for 3rd party use. Please be advised +This is a beta build, and is currently under active development. Please be advised of the following: -* 🛑 This code currently is not audited. 🛑 -* ❌ This is a public, active branch with **no support**. -* ❌ The code **does not have** documentation that is reviewed and approved by our Documentation team. -* ❌ The code **does not have** adequate unit tests, acceptance tests and stress tests. -* ❌ The code **does not have** automated tests that use the officially supported CI system. -* ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. -* :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this. -* :heavy_check_mark: The library **is not** majorly broken in some way. -* :heavy_check_mark: The library **does run** on mainnet and testnet. -* ❌ We **are actively rebasing** this branch and adding features where/when needed. -* ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.). -* :heavy_check_mark: There is a product manager for this library. -* :heavy_check_mark: Electric Coin Company maintains the library as we discover bugs and do network upgrades/minor releases. -* :heavy_check_mark: Users can expect to get a response within a few weeks after submitting an issue. -* ❌ The User Support team **has not yet been briefed** on the features provided to users and the functionality of the associated test-framework. -* ❌ The code is **not fully-documented**. - - -### 🛑 Use of this code may lead to a loss of funds 🛑 - -Use of this code in its current form or with modifications may lead to loss of funds, loss -of "expected" privacy, or denial of service for a large portion of users, or a bug which -could leverage any of those kinds of attacks (especially a "0 day" where we suspect few -people know about the vulnerability). - -### :eyes: At this time, this is for preview purposes only. :eyes: +* This code currently is not audited by an external security auditor, use it at + your own risk. +* The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. +* We **are actively changing** the codebase and adding features where/when needed. ---- From 3f06d92eec46b289cf173ce57def138768d7efcb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 27 Aug 2020 23:25:20 +0100 Subject: [PATCH 193/210] Depend on published versions of subtree crates --- zcash_client_backend/Cargo.toml | 8 ++++---- zcash_client_sqlite/Cargo.toml | 6 +++--- zcash_primitives/Cargo.toml | 8 ++++---- zcash_proofs/Cargo.toml | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 140d6d57b7..a4ce4290ec 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,12 +13,12 @@ edition = "2018" [dependencies] bech32 = "0.7" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } +ff = "0.7" +group = "0.7" hex = "0.4" -jubjub = { version = "0.4", path = "../jubjub" } +jubjub = "0.4" protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" zcash_primitives = { version = "0.3", path = "../zcash_primitives" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 32b5db3ca7..d0979aa79a 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -14,9 +14,9 @@ edition = "2018" [dependencies] bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } -jubjub = { version = "0.4", path = "../jubjub" } +ff = "0.7" +group = "0.7" +jubjub = "0.4" protobuf = "2" rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index c60d94f326..393ab55cdd 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -18,15 +18,15 @@ all-features = true aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } -ff = { version = "0.7", path = "../ff" } +ff = "0.7" fpe = "0.3" -group = { version = "0.7", path = "../group" } +group = "0.7" hex = "0.4" -jubjub = { version = "0.4", path = "../jubjub" } +jubjub = "0.4" lazy_static = "1" log = "0.4" rand = "0.7" diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 6dc1bbdd90..b1b83b768b 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -15,14 +15,14 @@ edition = "2018" all-features = true [dependencies] -bellman = { version = "0.7", path = "../bellman", default-features = false, features = ["groth16"] } +bellman = { version = "0.7", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" byteorder = "1" directories = { version = "3", optional = true } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } -jubjub = { version = "0.4", path = "../jubjub" } +ff = "0.7" +group = "0.7" +jubjub = "0.4" lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } rand_core = "0.5.1" From 3a4ea5da18e3e2703c6afba1c11ee684fcb3f306 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 27 Aug 2020 23:25:47 +0100 Subject: [PATCH 194/210] Remove subtree crates Now that we have finished the large refactor, we can make subsequent refactoring changes within the individual crates, and propagate the changes into the Zcash crates via normal dependency updates. --- Cargo.toml | 6 - bellman/.gitignore | 2 - bellman/COPYRIGHT | 14 - bellman/Cargo.toml | 44 - bellman/LICENSE-APACHE | 201 -- bellman/LICENSE-MIT | 23 - bellman/README.md | 33 - bellman/src/domain.rs | 493 ---- bellman/src/gadgets.rs | 33 - bellman/src/gadgets/blake2s.rs | 697 ------ bellman/src/gadgets/boolean.rs | 1824 -------------- bellman/src/gadgets/lookup.rs | 319 --- bellman/src/gadgets/multieq.rs | 123 - bellman/src/gadgets/multipack.rs | 111 - bellman/src/gadgets/num.rs | 591 ----- bellman/src/gadgets/sha256.rs | 388 --- bellman/src/gadgets/test/mod.rs | 463 ---- bellman/src/gadgets/uint32.rs | 767 ------ bellman/src/groth16/generator.rs | 501 ---- bellman/src/groth16/mod.rs | 567 ----- bellman/src/groth16/prover.rs | 339 --- bellman/src/groth16/tests/dummy_engine.rs | 491 ---- bellman/src/groth16/tests/mod.rs | 381 --- bellman/src/groth16/verifier.rs | 56 - bellman/src/lib.rs | 575 ----- bellman/src/multicore.rs | 164 -- bellman/src/multiexp.rs | 340 --- bellman/tests/mimc.rs | 226 -- bls12_381/.github/workflows/ci.yml | 105 - bls12_381/.gitignore | 3 - bls12_381/COPYRIGHT | 14 - bls12_381/Cargo.toml | 62 - bls12_381/LICENSE-APACHE | 201 -- bls12_381/LICENSE-MIT | 23 - bls12_381/README.md | 64 - bls12_381/RELEASES.md | 44 - bls12_381/benches/groups.rs | 170 -- bls12_381/katex-header.html | 15 - bls12_381/rust-toolchain | 1 - bls12_381/src/fp.rs | 916 ------- bls12_381/src/fp12.rs | 643 ----- bls12_381/src/fp2.rs | 875 ------- bls12_381/src/fp6.rs | 513 ---- bls12_381/src/g1.rs | 1678 ------------- bls12_381/src/g2.rs | 2142 ----------------- bls12_381/src/lib.rs | 81 - bls12_381/src/notes/design.rs | 62 - bls12_381/src/notes/serialization.rs | 29 - bls12_381/src/pairings.rs | 886 ------- bls12_381/src/scalar.rs | 1174 --------- .../g1_compressed_valid_test_vectors.dat | Bin 48000 -> 0 bytes .../g1_uncompressed_valid_test_vectors.dat | Bin 96000 -> 0 bytes .../g2_compressed_valid_test_vectors.dat | Bin 96000 -> 0 bytes .../g2_uncompressed_valid_test_vectors.dat | Bin 192000 -> 0 bytes bls12_381/src/tests/mod.rs | 230 -- bls12_381/src/util.rs | 174 -- ff/.gitignore | 3 - ff/Cargo.toml | 28 - ff/LICENSE-APACHE | 202 -- ff/LICENSE-MIT | 21 - ff/README.md | 67 - ff/ff_derive/Cargo.toml | 28 - ff/ff_derive/src/lib.rs | 1328 ---------- ff/ff_derive/src/pow_fixed.rs | 56 - ff/src/lib.rs | 343 --- group/.gitignore | 3 - group/COPYRIGHT | 14 - group/Cargo.toml | 25 - group/LICENSE-APACHE | 201 -- group/LICENSE-MIT | 23 - group/README.md | 20 - group/src/cofactor.rs | 119 - group/src/lib.rs | 164 -- group/src/prime.rs | 52 - group/src/tests/mod.rs | 447 ---- group/src/wnaf.rs | 214 -- jubjub/.github/workflows/ci.yml | 95 - jubjub/.gitignore | 3 - jubjub/COPYRIGHT | 14 - jubjub/Cargo.toml | 63 - jubjub/LICENSE-APACHE | 201 -- jubjub/LICENSE-MIT | 23 - jubjub/README.md | 53 - jubjub/RELEASES.md | 53 - jubjub/benches/fq_bench.rs | 58 - jubjub/benches/fr_bench.rs | 58 - jubjub/benches/point_bench.rs | 73 - jubjub/doc/derive/.gitignore | 1 - jubjub/doc/derive/derive.sage | 32 - jubjub/doc/evidence/.gitignore | 102 - jubjub/doc/evidence/LICENSE | 19 - jubjub/doc/evidence/README.md | 28 - jubjub/doc/evidence/a | 1 - jubjub/doc/evidence/d | 1 - jubjub/doc/evidence/l | 1 - jubjub/doc/evidence/p | 1 - jubjub/doc/evidence/rigid | 1 - jubjub/doc/evidence/run.sh | 4 - jubjub/doc/evidence/shape | 1 - jubjub/doc/evidence/verify.sage | 444 ---- jubjub/doc/evidence/x0 | 1 - jubjub/doc/evidence/x1 | 1 - jubjub/doc/evidence/y0 | 1 - jubjub/doc/evidence/y1 | 1 - jubjub/src/fr.rs | 1132 --------- jubjub/src/lib.rs | 1756 -------------- jubjub/src/util.rs | 174 -- jubjub/tests/common.rs | 29 - jubjub/tests/fq_blackbox.rs | 120 - jubjub/tests/fr_blackbox.rs | 120 - pairing/.gitignore | 3 - pairing/COPYRIGHT | 14 - pairing/Cargo.toml | 36 - pairing/LICENSE-APACHE | 201 -- pairing/LICENSE-MIT | 23 - pairing/README.md | 34 - pairing/src/lib.rs | 115 - pairing/src/tests/engine.rs | 130 - pairing/src/tests/field.rs | 243 -- pairing/src/tests/mod.rs | 3 - pairing/src/tests/repr.rs | 23 - 121 files changed, 28725 deletions(-) delete mode 100644 bellman/.gitignore delete mode 100644 bellman/COPYRIGHT delete mode 100644 bellman/Cargo.toml delete mode 100644 bellman/LICENSE-APACHE delete mode 100644 bellman/LICENSE-MIT delete mode 100644 bellman/README.md delete mode 100644 bellman/src/domain.rs delete mode 100644 bellman/src/gadgets.rs delete mode 100644 bellman/src/gadgets/blake2s.rs delete mode 100644 bellman/src/gadgets/boolean.rs delete mode 100644 bellman/src/gadgets/lookup.rs delete mode 100644 bellman/src/gadgets/multieq.rs delete mode 100644 bellman/src/gadgets/multipack.rs delete mode 100644 bellman/src/gadgets/num.rs delete mode 100644 bellman/src/gadgets/sha256.rs delete mode 100644 bellman/src/gadgets/test/mod.rs delete mode 100644 bellman/src/gadgets/uint32.rs delete mode 100644 bellman/src/groth16/generator.rs delete mode 100644 bellman/src/groth16/mod.rs delete mode 100644 bellman/src/groth16/prover.rs delete mode 100644 bellman/src/groth16/tests/dummy_engine.rs delete mode 100644 bellman/src/groth16/tests/mod.rs delete mode 100644 bellman/src/groth16/verifier.rs delete mode 100644 bellman/src/lib.rs delete mode 100644 bellman/src/multicore.rs delete mode 100644 bellman/src/multiexp.rs delete mode 100644 bellman/tests/mimc.rs delete mode 100644 bls12_381/.github/workflows/ci.yml delete mode 100644 bls12_381/.gitignore delete mode 100644 bls12_381/COPYRIGHT delete mode 100644 bls12_381/Cargo.toml delete mode 100644 bls12_381/LICENSE-APACHE delete mode 100644 bls12_381/LICENSE-MIT delete mode 100644 bls12_381/README.md delete mode 100644 bls12_381/RELEASES.md delete mode 100644 bls12_381/benches/groups.rs delete mode 100644 bls12_381/katex-header.html delete mode 100644 bls12_381/rust-toolchain delete mode 100644 bls12_381/src/fp.rs delete mode 100644 bls12_381/src/fp12.rs delete mode 100644 bls12_381/src/fp2.rs delete mode 100644 bls12_381/src/fp6.rs delete mode 100644 bls12_381/src/g1.rs delete mode 100644 bls12_381/src/g2.rs delete mode 100644 bls12_381/src/lib.rs delete mode 100644 bls12_381/src/notes/design.rs delete mode 100644 bls12_381/src/notes/serialization.rs delete mode 100644 bls12_381/src/pairings.rs delete mode 100644 bls12_381/src/scalar.rs delete mode 100644 bls12_381/src/tests/g1_compressed_valid_test_vectors.dat delete mode 100644 bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat delete mode 100644 bls12_381/src/tests/g2_compressed_valid_test_vectors.dat delete mode 100644 bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat delete mode 100644 bls12_381/src/tests/mod.rs delete mode 100644 bls12_381/src/util.rs delete mode 100644 ff/.gitignore delete mode 100644 ff/Cargo.toml delete mode 100644 ff/LICENSE-APACHE delete mode 100644 ff/LICENSE-MIT delete mode 100644 ff/README.md delete mode 100644 ff/ff_derive/Cargo.toml delete mode 100644 ff/ff_derive/src/lib.rs delete mode 100644 ff/ff_derive/src/pow_fixed.rs delete mode 100644 ff/src/lib.rs delete mode 100644 group/.gitignore delete mode 100644 group/COPYRIGHT delete mode 100644 group/Cargo.toml delete mode 100644 group/LICENSE-APACHE delete mode 100644 group/LICENSE-MIT delete mode 100644 group/README.md delete mode 100644 group/src/cofactor.rs delete mode 100644 group/src/lib.rs delete mode 100644 group/src/prime.rs delete mode 100644 group/src/tests/mod.rs delete mode 100644 group/src/wnaf.rs delete mode 100644 jubjub/.github/workflows/ci.yml delete mode 100644 jubjub/.gitignore delete mode 100644 jubjub/COPYRIGHT delete mode 100644 jubjub/Cargo.toml delete mode 100644 jubjub/LICENSE-APACHE delete mode 100644 jubjub/LICENSE-MIT delete mode 100644 jubjub/README.md delete mode 100644 jubjub/RELEASES.md delete mode 100644 jubjub/benches/fq_bench.rs delete mode 100644 jubjub/benches/fr_bench.rs delete mode 100644 jubjub/benches/point_bench.rs delete mode 100644 jubjub/doc/derive/.gitignore delete mode 100644 jubjub/doc/derive/derive.sage delete mode 100644 jubjub/doc/evidence/.gitignore delete mode 100644 jubjub/doc/evidence/LICENSE delete mode 100644 jubjub/doc/evidence/README.md delete mode 100644 jubjub/doc/evidence/a delete mode 100644 jubjub/doc/evidence/d delete mode 100644 jubjub/doc/evidence/l delete mode 100644 jubjub/doc/evidence/p delete mode 100644 jubjub/doc/evidence/rigid delete mode 100644 jubjub/doc/evidence/run.sh delete mode 100644 jubjub/doc/evidence/shape delete mode 100644 jubjub/doc/evidence/verify.sage delete mode 100644 jubjub/doc/evidence/x0 delete mode 100644 jubjub/doc/evidence/x1 delete mode 100644 jubjub/doc/evidence/y0 delete mode 100644 jubjub/doc/evidence/y1 delete mode 100644 jubjub/src/fr.rs delete mode 100644 jubjub/src/lib.rs delete mode 100644 jubjub/src/util.rs delete mode 100644 jubjub/tests/common.rs delete mode 100644 jubjub/tests/fq_blackbox.rs delete mode 100644 jubjub/tests/fr_blackbox.rs delete mode 100644 pairing/.gitignore delete mode 100644 pairing/COPYRIGHT delete mode 100644 pairing/Cargo.toml delete mode 100644 pairing/LICENSE-APACHE delete mode 100644 pairing/LICENSE-MIT delete mode 100644 pairing/README.md delete mode 100644 pairing/src/lib.rs delete mode 100644 pairing/src/tests/engine.rs delete mode 100644 pairing/src/tests/field.rs delete mode 100644 pairing/src/tests/mod.rs delete mode 100644 pairing/src/tests/repr.rs diff --git a/Cargo.toml b/Cargo.toml index 2bd30c808b..b1943e06ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,11 @@ [workspace] members = [ - "bellman", "components/equihash", - "ff", - "group", - "pairing", "zcash_client_backend", "zcash_client_sqlite", "zcash_history", "zcash_primitives", "zcash_proofs", - "jubjub", - "bls12_381", ] [profile.release] diff --git a/bellman/.gitignore b/bellman/.gitignore deleted file mode 100644 index a9d37c560c..0000000000 --- a/bellman/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/bellman/COPYRIGHT b/bellman/COPYRIGHT deleted file mode 100644 index 8b5f8cf37e..0000000000 --- a/bellman/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "bellman" library are retained by their contributors. No -copyright assignment is required to contribute to the "bellman" library. - -The "bellman" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml deleted file mode 100644 index a6f02b3a40..0000000000 --- a/bellman/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -authors = ["Sean Bowe "] -description = "zk-SNARK library" -readme = "README.md" -homepage = "https://github.com/ebfull/bellman" -license = "MIT/Apache-2.0" -name = "bellman" -repository = "https://github.com/ebfull/bellman" -version = "0.7.0" -edition = "2018" - -[dependencies] -bit-vec = "0.6" -blake2s_simd = "0.5" -ff = { version = "0.7", path = "../ff" } -futures = "0.1" -futures-cpupool = { version = "0.1", optional = true } -group = { version = "0.7", path = "../group" } -num_cpus = { version = "1", optional = true } -crossbeam = { version = "0.7", optional = true } -pairing = { version = "0.17", path = "../pairing", optional = true } -rand_core = "0.5" -byteorder = "1" -subtle = "2.2.1" - -[dev-dependencies] -bls12_381 = { version = "0.2", path = "../bls12_381" } -hex-literal = "0.2" -rand = "0.7" -rand_xorshift = "0.2" -sha2 = "0.9" - -[features] -groth16 = ["pairing"] -multicore = ["futures-cpupool", "crossbeam", "num_cpus"] -default = ["groth16", "multicore"] - -[[test]] -name = "mimc" -path = "tests/mimc.rs" -required-features = ["groth16"] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/bellman/LICENSE-APACHE b/bellman/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/bellman/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/bellman/LICENSE-MIT b/bellman/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/bellman/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/bellman/README.md b/bellman/README.md deleted file mode 100644 index 850f2e3e67..0000000000 --- a/bellman/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # - -`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits -and primitive structures, as well as basic gadget implementations such as -booleans and number abstractions. - -`bellman` uses the `ff` and `group` crates to build circuits generically over a -scalar field type, which is used as the "word" of a circuit. Arithmetic -operations modulo the scalar field's prime are efficient, while other operations -(such as boolean logic) are implemented using these words. - -## Roadmap - -Currently `bellman` bundles an implementation of the Groth16 proving system. -This will be moved into a separate crate in the future, and `bellman` will -contain any utilities that make implementing proving systems easier. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs deleted file mode 100644 index 91a8b83552..0000000000 --- a/bellman/src/domain.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! This module contains an [`EvaluationDomain`] abstraction for performing -//! various kinds of polynomial arithmetic on top of the scalar field. -//! -//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient -//! polynomial over a target polynomial with roots at distinct points associated -//! with each constraint of the constraint system. In order to be efficient, we -//! choose these roots to be the powers of a 2n root of unity in the -//! field. This allows us to perform polynomial operations in O(n) by performing -//! an O(n log n) FFT over such a domain. -//! -//! [`EvaluationDomain`]: crate::domain::EvaluationDomain -//! [Groth16]: https://eprint.iacr.org/2016/260 - -use ff::PrimeField; -use group::cofactor::CofactorCurve; - -use super::SynthesisError; - -use super::multicore::Worker; - -pub struct EvaluationDomain> { - coeffs: Vec, - exp: u32, - omega: S, - omegainv: S, - geninv: S, - minv: S, -} - -impl> AsRef<[G]> for EvaluationDomain { - fn as_ref(&self) -> &[G] { - &self.coeffs - } -} - -impl> AsMut<[G]> for EvaluationDomain { - fn as_mut(&mut self) -> &mut [G] { - &mut self.coeffs - } -} - -impl> EvaluationDomain { - pub fn into_coeffs(self) -> Vec { - self.coeffs - } - - pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { - // Compute the size of our evaluation domain - let mut m = 1; - let mut exp = 0; - while m < coeffs.len() { - m *= 2; - exp += 1; - - // The pairing-friendly curve may not be able to support - // large enough (radix2) evaluation domains. - if exp >= S::S { - return Err(SynthesisError::PolynomialDegreeTooLarge); - } - } - - // Compute omega, the 2^exp primitive root of unity - let mut omega = S::root_of_unity(); - for _ in exp..S::S { - omega = omega.square(); - } - - // Extend the coeffs vector with zeroes if necessary - coeffs.resize(m, G::group_zero()); - - Ok(EvaluationDomain { - coeffs, - exp, - omega, - omegainv: omega.invert().unwrap(), - geninv: S::multiplicative_generator().invert().unwrap(), - minv: S::from_str(&format!("{}", m)).unwrap().invert().unwrap(), - }) - } - - pub fn fft(&mut self, worker: &Worker) { - best_fft(&mut self.coeffs, worker, &self.omega, self.exp); - } - - pub fn ifft(&mut self, worker: &Worker) { - best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); - - worker.scope(self.coeffs.len(), |scope, chunk| { - let minv = self.minv; - - for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move |_scope| { - for v in v { - v.group_mul_assign(&minv); - } - }); - } - }); - } - - pub fn distribute_powers(&mut self, worker: &Worker, g: S) { - worker.scope(self.coeffs.len(), |scope, chunk| { - for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { - scope.spawn(move |_scope| { - let mut u = g.pow_vartime(&[(i * chunk) as u64]); - for v in v.iter_mut() { - v.group_mul_assign(&u); - u.mul_assign(&g); - } - }); - } - }); - } - - pub fn coset_fft(&mut self, worker: &Worker) { - self.distribute_powers(worker, S::multiplicative_generator()); - self.fft(worker); - } - - pub fn icoset_fft(&mut self, worker: &Worker) { - let geninv = self.geninv; - - self.ifft(worker); - self.distribute_powers(worker, geninv); - } - - /// This evaluates t(tau) for this domain, which is - /// tau^m - 1 for these radix-2 domains. - pub fn z(&self, tau: &S) -> S { - let mut tmp = tau.pow_vartime(&[self.coeffs.len() as u64]); - tmp.sub_assign(&S::one()); - - tmp - } - - /// The target polynomial is the zero polynomial in our - /// evaluation domain, so we must perform division over - /// a coset. - pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { - let i = self.z(&S::multiplicative_generator()).invert().unwrap(); - - worker.scope(self.coeffs.len(), |scope, chunk| { - for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move |_scope| { - for v in v { - v.group_mul_assign(&i); - } - }); - } - }); - } - - /// Perform O(n) multiplication of two polynomials in the domain. - pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain>) { - assert_eq!(self.coeffs.len(), other.coeffs.len()); - - worker.scope(self.coeffs.len(), |scope, chunk| { - for (a, b) in self - .coeffs - .chunks_mut(chunk) - .zip(other.coeffs.chunks(chunk)) - { - scope.spawn(move |_scope| { - for (a, b) in a.iter_mut().zip(b.iter()) { - a.group_mul_assign(&b.0); - } - }); - } - }); - } - - /// Perform O(n) subtraction of one polynomial from another in the domain. - pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain) { - assert_eq!(self.coeffs.len(), other.coeffs.len()); - - worker.scope(self.coeffs.len(), |scope, chunk| { - for (a, b) in self - .coeffs - .chunks_mut(chunk) - .zip(other.coeffs.chunks(chunk)) - { - scope.spawn(move |_scope| { - for (a, b) in a.iter_mut().zip(b.iter()) { - a.group_sub_assign(&b); - } - }); - } - }); - } -} - -pub trait Group: Sized + Copy + Clone + Send + Sync { - fn group_zero() -> Self; - fn group_mul_assign(&mut self, by: &Scalar); - fn group_add_assign(&mut self, other: &Self); - fn group_sub_assign(&mut self, other: &Self); -} - -pub struct Point(pub G); - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - self.0 == other.0 - } -} - -impl Copy for Point {} - -impl Clone for Point { - fn clone(&self) -> Point { - *self - } -} - -impl Group for Point { - fn group_zero() -> Self { - Point(G::identity()) - } - fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -pub struct Scalar(pub S); - -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { - self.0 == other.0 - } -} - -impl Copy for Scalar {} - -impl Clone for Scalar { - fn clone(&self) -> Scalar { - *self - } -} - -impl Group for Scalar { - fn group_zero() -> Self { - Scalar(S::zero()) - } - fn group_mul_assign(&mut self, by: &S) { - self.0.mul_assign(by); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -fn best_fft>(a: &mut [T], worker: &Worker, omega: &S, log_n: u32) { - let log_cpus = worker.log_num_cpus(); - - if log_n <= log_cpus { - serial_fft(a, omega, log_n); - } else { - parallel_fft(a, worker, omega, log_n, log_cpus); - } -} - -fn serial_fft>(a: &mut [T], omega: &S, log_n: u32) { - fn bitreverse(mut n: u32, l: u32) -> u32 { - let mut r = 0; - for _ in 0..l { - r = (r << 1) | (n & 1); - n >>= 1; - } - r - } - - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - for k in 0..n { - let rk = bitreverse(k, log_n); - if k < rk { - a.swap(rk as usize, k as usize); - } - } - - let mut m = 1; - for _ in 0..log_n { - let w_m = omega.pow_vartime(&[u64::from(n / (2 * m))]); - - let mut k = 0; - while k < n { - let mut w = S::one(); - for j in 0..m { - let mut t = a[(k + j + m) as usize]; - t.group_mul_assign(&w); - let mut tmp = a[(k + j) as usize]; - tmp.group_sub_assign(&t); - a[(k + j + m) as usize] = tmp; - a[(k + j) as usize].group_add_assign(&t); - w.mul_assign(&w_m); - } - - k += 2 * m; - } - - m *= 2; - } -} - -fn parallel_fft>( - a: &mut [T], - worker: &Worker, - omega: &S, - log_n: u32, - log_cpus: u32, -) { - assert!(log_n >= log_cpus); - - let num_cpus = 1 << log_cpus; - let log_new_n = log_n - log_cpus; - let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; - let new_omega = omega.pow_vartime(&[num_cpus as u64]); - - worker.scope(0, |scope, _| { - let a = &*a; - - for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move |_scope| { - // Shuffle into a sub-FFT - let omega_j = omega.pow_vartime(&[j as u64]); - let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]); - - let mut elt = S::one(); - for (i, tmp) in tmp.iter_mut().enumerate() { - for s in 0..num_cpus { - let idx = (i + (s << log_new_n)) % (1 << log_n); - let mut t = a[idx]; - t.group_mul_assign(&elt); - tmp.group_add_assign(&t); - elt.mul_assign(&omega_step); - } - elt.mul_assign(&omega_j); - } - - // Perform sub-FFT - serial_fft(tmp, &new_omega, log_new_n); - }); - } - }); - - // TODO: does this hurt or help? - worker.scope(a.len(), |scope, chunk| { - let tmp = &tmp; - - for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move |_scope| { - let mut idx = idx * chunk; - let mask = (1 << log_cpus) - 1; - for a in a { - *a = tmp[idx & mask][idx >> log_cpus]; - idx += 1; - } - }); - } - }); -} - -// Test multiplying various (low degree) polynomials together and -// comparing with naive evaluations. -#[cfg(feature = "pairing")] -#[test] -fn polynomial_arith() { - use bls12_381::Scalar as Fr; - use rand_core::RngCore; - - fn test_mul(rng: &mut R) { - let worker = Worker::new(); - - for coeffs_a in 0..70 { - for coeffs_b in 0..70 { - let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::(S::random(rng))).collect(); - let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::(S::random(rng))).collect(); - - // naive evaluation - let mut naive = vec![Scalar(S::zero()); coeffs_a + coeffs_b]; - for (i1, a) in a.iter().enumerate() { - for (i2, b) in b.iter().enumerate() { - let mut prod = *a; - prod.group_mul_assign(&b.0); - naive[i1 + i2].group_add_assign(&prod); - } - } - - a.resize(coeffs_a + coeffs_b, Scalar(S::zero())); - b.resize(coeffs_a + coeffs_b, Scalar(S::zero())); - - let mut a = EvaluationDomain::from_coeffs(a).unwrap(); - let mut b = EvaluationDomain::from_coeffs(b).unwrap(); - - a.fft(&worker); - b.fft(&worker); - a.mul_assign(&worker, &b); - a.ifft(&worker); - - for (naive, fft) in naive.iter().zip(a.coeffs.iter()) { - assert!(naive == fft); - } - } - } - } - - let rng = &mut rand::thread_rng(); - - test_mul::(rng); -} - -#[cfg(feature = "pairing")] -#[test] -fn fft_composition() { - use bls12_381::Scalar as Fr; - use rand_core::RngCore; - - fn test_comp(rng: &mut R) { - let worker = Worker::new(); - - for coeffs in 0..10 { - let coeffs = 1 << coeffs; - - let mut v = vec![]; - for _ in 0..coeffs { - v.push(Scalar::(S::random(rng))); - } - - let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); - domain.ifft(&worker); - domain.fft(&worker); - assert!(v == domain.coeffs); - domain.fft(&worker); - domain.ifft(&worker); - assert!(v == domain.coeffs); - domain.icoset_fft(&worker); - domain.coset_fft(&worker); - assert!(v == domain.coeffs); - domain.coset_fft(&worker); - domain.icoset_fft(&worker); - assert!(v == domain.coeffs); - } - } - - let rng = &mut rand::thread_rng(); - - test_comp::(rng); -} - -#[cfg(feature = "pairing")] -#[test] -fn parallel_fft_consistency() { - use bls12_381::Scalar as Fr; - use rand_core::RngCore; - use std::cmp::min; - - fn test_consistency(rng: &mut R) { - let worker = Worker::new(); - - for _ in 0..5 { - for log_d in 0..10 { - let d = 1 << log_d; - - let v1 = (0..d) - .map(|_| Scalar::(S::random(rng))) - .collect::>(); - let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); - let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); - - for log_cpus in log_d..min(log_d + 1, 3) { - parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus); - serial_fft(&mut v2.coeffs, &v2.omega, log_d); - - assert!(v1.coeffs == v2.coeffs); - } - } - } - } - - let rng = &mut rand::thread_rng(); - - test_consistency::(rng); -} diff --git a/bellman/src/gadgets.rs b/bellman/src/gadgets.rs deleted file mode 100644 index b0ce734722..0000000000 --- a/bellman/src/gadgets.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Self-contained sub-circuit implementations for various primitives. - -pub mod test; - -pub mod blake2s; -pub mod boolean; -pub mod lookup; -pub mod multieq; -pub mod multipack; -pub mod num; -pub mod sha256; -pub mod uint32; - -use crate::SynthesisError; - -// TODO: This should probably be removed and we -// should use existing helper methods on `Option` -// for mapping with an error. -/// This basically is just an extension to `Option` -/// which allows for a convenient mapping to an -/// error on `None`. -pub trait Assignment { - fn get(&self) -> Result<&T, SynthesisError>; -} - -impl Assignment for Option { - fn get(&self) -> Result<&T, SynthesisError> { - match *self { - Some(ref v) => Ok(v), - None => Err(SynthesisError::AssignmentMissing), - } - } -} diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs deleted file mode 100644 index f5e46eaae4..0000000000 --- a/bellman/src/gadgets/blake2s.rs +++ /dev/null @@ -1,697 +0,0 @@ -//! The [BLAKE2s] hash function with personalization support. -//! -//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693 - -use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32}; -use crate::{ConstraintSystem, SynthesisError}; -use ff::PrimeField; - -/* -2.1. Parameters - The following table summarizes various parameters and their ranges: - | BLAKE2b | BLAKE2s | - --------------+------------------+------------------+ - Bits in word | w = 64 | w = 32 | - Rounds in F | r = 12 | r = 10 | - Block bytes | bb = 128 | bb = 64 | - Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 | - Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 | - Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 | - --------------+------------------+------------------+ - G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) | - constants = | (32, 24, 16, 63) | (16, 12, 8, 7) | - --------------+------------------+------------------+ -*/ - -const R1: usize = 16; -const R2: usize = 12; -const R3: usize = 8; -const R4: usize = 7; - -/* - Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - ----------+-------------------------------------------------+ - SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 | - SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 | - SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 | - SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 | - SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 | - SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 | - SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 | - SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 | - SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 | - ----------+-------------------------------------------------+ -*/ - -const SIGMA: [[usize; 16]; 10] = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], -]; - -/* -3.1. Mixing Function G - The G primitive function mixes two input words, "x" and "y", into - four words indexed by "a", "b", "c", and "d" in the working vector - v[0..15]. The full modified vector is returned. The rotation - constants (R1, R2, R3, R4) are given in Section 2.1. - FUNCTION G( v[0..15], a, b, c, d, x, y ) - | - | v[a] := (v[a] + v[b] + x) mod 2**w - | v[d] := (v[d] ^ v[a]) >>> R1 - | v[c] := (v[c] + v[d]) mod 2**w - | v[b] := (v[b] ^ v[c]) >>> R2 - | v[a] := (v[a] + v[b] + y) mod 2**w - | v[d] := (v[d] ^ v[a]) >>> R3 - | v[c] := (v[c] + v[d]) mod 2**w - | v[b] := (v[b] ^ v[c]) >>> R4 - | - | RETURN v[0..15] - | - END FUNCTION. -*/ - -fn mixing_g, M>( - mut cs: M, - v: &mut [UInt32], - a: usize, - b: usize, - c: usize, - d: usize, - x: &UInt32, - y: &UInt32, -) -> Result<(), SynthesisError> -where - M: ConstraintSystem>, -{ - v[a] = UInt32::addmany( - cs.namespace(|| "mixing step 1"), - &[v[a].clone(), v[b].clone(), x.clone()], - )?; - v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1); - v[c] = UInt32::addmany( - cs.namespace(|| "mixing step 3"), - &[v[c].clone(), v[d].clone()], - )?; - v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2); - v[a] = UInt32::addmany( - cs.namespace(|| "mixing step 5"), - &[v[a].clone(), v[b].clone(), y.clone()], - )?; - v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3); - v[c] = UInt32::addmany( - cs.namespace(|| "mixing step 7"), - &[v[c].clone(), v[d].clone()], - )?; - v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4); - - Ok(()) -} - -/* -3.2. Compression Function F - Compression function F takes as an argument the state vector "h", - message block vector "m" (last block is padded with zeros to full - block size, if required), 2w-bit offset counter "t", and final block - indicator flag "f". Local vector v[0..15] is used in processing. F - returns a new state vector. The number of rounds, "r", is 12 for - BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1. - FUNCTION F( h[0..7], m[0..15], t, f ) - | - | // Initialize local work vector v[0..15] - | v[0..7] := h[0..7] // First half from state. - | v[8..15] := IV[0..7] // Second half from IV. - | - | v[12] := v[12] ^ (t mod 2**w) // Low word of the offset. - | v[13] := v[13] ^ (t >> w) // High word. - | - | IF f = TRUE THEN // last block flag? - | | v[14] := v[14] ^ 0xFF..FF // Invert all bits. - | END IF. - | - | // Cryptographic mixing - | FOR i = 0 TO r - 1 DO // Ten or twelve rounds. - | | - | | // Message word selection permutation for this round. - | | s[0..15] := SIGMA[i mod 10][0..15] - | | - | | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] ) - | | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] ) - | | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] ) - | | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] ) - | | - | | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] ) - | | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] ) - | | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] ) - | | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] ) - | | - | END FOR - | - | FOR i = 0 TO 7 DO // XOR the two halves. - | | h[i] := h[i] ^ v[i] ^ v[i + 8] - | END FOR. - | - | RETURN h[0..7] // New state. - | - END FUNCTION. -*/ - -fn blake2s_compression>( - mut cs: CS, - h: &mut [UInt32], - m: &[UInt32], - t: u64, - f: bool, -) -> Result<(), SynthesisError> { - assert_eq!(h.len(), 8); - assert_eq!(m.len(), 16); - - /* - static const uint32_t blake2s_iv[8] = - { - 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, - 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 - }; - */ - - let mut v = Vec::with_capacity(16); - v.extend_from_slice(h); - v.push(UInt32::constant(0x6A09E667)); - v.push(UInt32::constant(0xBB67AE85)); - v.push(UInt32::constant(0x3C6EF372)); - v.push(UInt32::constant(0xA54FF53A)); - v.push(UInt32::constant(0x510E527F)); - v.push(UInt32::constant(0x9B05688C)); - v.push(UInt32::constant(0x1F83D9AB)); - v.push(UInt32::constant(0x5BE0CD19)); - - assert_eq!(v.len(), 16); - - v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?; - v[13] = v[13].xor( - cs.namespace(|| "second xor"), - &UInt32::constant((t >> 32) as u32), - )?; - - if f { - v[14] = v[14].xor( - cs.namespace(|| "third xor"), - &UInt32::constant(u32::max_value()), - )?; - } - - { - let mut cs = MultiEq::new(&mut cs); - - for i in 0..10 { - let mut cs = cs.namespace(|| format!("round {}", i)); - - let s = SIGMA[i % 10]; - - mixing_g( - cs.namespace(|| "mixing invocation 1"), - &mut v, - 0, - 4, - 8, - 12, - &m[s[0]], - &m[s[1]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 2"), - &mut v, - 1, - 5, - 9, - 13, - &m[s[2]], - &m[s[3]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 3"), - &mut v, - 2, - 6, - 10, - 14, - &m[s[4]], - &m[s[5]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 4"), - &mut v, - 3, - 7, - 11, - 15, - &m[s[6]], - &m[s[7]], - )?; - - mixing_g( - cs.namespace(|| "mixing invocation 5"), - &mut v, - 0, - 5, - 10, - 15, - &m[s[8]], - &m[s[9]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 6"), - &mut v, - 1, - 6, - 11, - 12, - &m[s[10]], - &m[s[11]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 7"), - &mut v, - 2, - 7, - 8, - 13, - &m[s[12]], - &m[s[13]], - )?; - mixing_g( - cs.namespace(|| "mixing invocation 8"), - &mut v, - 3, - 4, - 9, - 14, - &m[s[14]], - &m[s[15]], - )?; - } - } - - for i in 0..8 { - let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i)); - - h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?; - h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?; - } - - Ok(()) -} - -/* - FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn ) - | - | h[0..7] := IV[0..7] // Initialization Vector. - | - | // Parameter block p[0] - | h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn - | - | // Process padded key and data blocks - | IF dd > 1 THEN - | | FOR i = 0 TO dd - 2 DO - | | | h := F( h, d[i], (i + 1) * bb, FALSE ) - | | END FOR. - | END IF. - | - | // Final block. - | IF kk = 0 THEN - | | h := F( h, d[dd - 1], ll, TRUE ) - | ELSE - | | h := F( h, d[dd - 1], ll + bb, TRUE ) - | END IF. - | - | RETURN first "nn" bytes from little-endian word array h[]. - | - END FUNCTION. -*/ - -pub fn blake2s>( - mut cs: CS, - input: &[Boolean], - personalization: &[u8], -) -> Result, SynthesisError> { - use byteorder::{ByteOrder, LittleEndian}; - - assert_eq!(personalization.len(), 8); - assert!(input.len() % 8 == 0); - - let mut h = Vec::with_capacity(8); - h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32)); - h.push(UInt32::constant(0xBB67AE85)); - h.push(UInt32::constant(0x3C6EF372)); - h.push(UInt32::constant(0xA54FF53A)); - h.push(UInt32::constant(0x510E527F)); - h.push(UInt32::constant(0x9B05688C)); - - // Personalization is stored here - h.push(UInt32::constant( - 0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]), - )); - h.push(UInt32::constant( - 0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]), - )); - - let mut blocks: Vec> = vec![]; - - for block in input.chunks(512) { - let mut this_block = Vec::with_capacity(16); - for word in block.chunks(32) { - let mut tmp = word.to_vec(); - while tmp.len() < 32 { - tmp.push(Boolean::constant(false)); - } - this_block.push(UInt32::from_bits(&tmp)); - } - while this_block.len() < 16 { - this_block.push(UInt32::constant(0)); - } - blocks.push(this_block); - } - - if blocks.is_empty() { - blocks.push((0..16).map(|_| UInt32::constant(0)).collect()); - } - - for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() { - let cs = cs.namespace(|| format!("block {}", i)); - - blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?; - } - - { - let cs = cs.namespace(|| "final block"); - - blake2s_compression( - cs, - &mut h, - &blocks[blocks.len() - 1], - (input.len() / 8) as u64, - true, - )?; - } - - Ok(h.into_iter().flat_map(|b| b.into_bits()).collect()) -} - -#[cfg(test)] -mod test { - use blake2s_simd::Params as Blake2sParams; - use bls12_381::Scalar; - use hex_literal::hex; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - use super::blake2s; - use crate::gadgets::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::TestConstraintSystem; - use crate::ConstraintSystem; - - #[test] - fn test_blank_hash() { - let mut cs = TestConstraintSystem::::new(); - let input_bits = vec![]; - let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - // >>> import blake2s from hashlib - // >>> h = blake2s(digest_size=32, person=b'12345678') - // >>> h.hexdigest() - let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); - - let mut out = out.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_constraints() { - let mut cs = TestConstraintSystem::::new(); - let input_bits: Vec<_> = (0..512) - .map(|i| { - AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) - .unwrap() - .into() - }) - .collect(); - blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 21518); - } - - #[test] - fn test_blake2s_precomp_constraints() { - // Test that 512 fixed leading bits (constants) - // doesn't result in more constraints. - - let mut cs = TestConstraintSystem::::new(); - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let input_bits: Vec<_> = (0..512) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .chain((0..512).map(|i| { - AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) - .unwrap() - .into() - })) - .collect(); - blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 21518); - } - - #[test] - fn test_blake2s_constant_constraints() { - let mut cs = TestConstraintSystem::::new(); - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let input_bits: Vec<_> = (0..512) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect(); - blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert_eq!(cs.num_constraints(), 0); - } - - #[test] - fn test_blake2s() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(b"12345678") - .to_state(); - - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .as_ref() - .iter() - .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); - - for b in r { - match b { - Boolean::Is(b) => { - assert!(s.next().unwrap() == b.get_value().unwrap()); - } - Boolean::Not(b) => { - assert!(s.next().unwrap() != b.get_value().unwrap()); - } - Boolean::Constant(b) => { - assert!(input_len == 0); - assert!(s.next().unwrap() == b); - } - } - } - } - } - - #[test] - fn test_blake2s_256_vars() { - let data: Vec = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); - assert_eq!(data.len(), 256); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e"); - - let mut out = r.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_700_vars() { - let data: Vec = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); - assert_eq!(data.len(), 700); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a"); - - let mut out = r.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_test_vectors() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let expecteds = [ - hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"), - hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"), - ]; - for i in 0..2 { - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(b"12345678") - .to_state(); - let input_len = 1024; - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .as_ref() - .iter() - .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); - - for b in r { - match b { - Boolean::Is(b) => { - assert!(s.next().unwrap() == b.get_value().unwrap()); - } - Boolean::Not(b) => { - assert!(s.next().unwrap() != b.get_value().unwrap()); - } - Boolean::Constant(b) => { - assert!(input_len == 0); - assert!(s.next().unwrap() == b); - } - } - } - - assert_eq!(expecteds[i], hash_result.as_bytes()); - } - } -} diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs deleted file mode 100644 index e5e24b6b69..0000000000 --- a/bellman/src/gadgets/boolean.rs +++ /dev/null @@ -1,1824 +0,0 @@ -//! Gadgets for allocating bits in the circuit and performing boolean logic. - -use ff::{BitIterator, PrimeField}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use super::Assignment; - -/// Represents a variable in the constraint system which is guaranteed -/// to be either zero or one. -#[derive(Clone)] -pub struct AllocatedBit { - variable: Variable, - value: Option, -} - -impl AllocatedBit { - pub fn get_value(&self) -> Option { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. Further, constrain that the boolean is false - /// unless the condition is false. - pub fn alloc_conditionally( - mut cs: CS, - value: Option, - must_be_false: &AllocatedBit, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain: (1 - must_be_false - a) * a = 0 - // if must_be_false is true, the equation - // reduces to -a * a = 0, which implies a = 0. - // if must_be_false is false, the equation - // reduces to (1 - a) * a = 0, which is a - // traditional boolean constraint. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - must_be_false.variable - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. - pub fn alloc(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Performs an XOR operation over the two operands, returning - /// an `AllocatedBit`. - pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "xor result", - || { - if *a.value.get()? ^ *b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a + a) * (b) = (a + b - c) - // Given that a and b are boolean constrained, if they - // are equal, the only solution for c is 0, and if they - // are different, the only solution for c is 1. - // - // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c - // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c - // (1 - ab) * (1 - (1 - a - b + ab)) = c - // (1 - ab) * (a + b - ab) = c - // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c - // a + b - ab - ab - ab + ab = c - // a + b - 2ab = c - // -2a * b = c - a - b - // 2a * b = a + b - c - // (a + a) * b = a + b - c - cs.enforce( - || "xor constraint", - |lc| lc + a.variable + a.variable, - |lc| lc + b.variable, - |lc| lc + a.variable + b.variable - result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Performs an AND operation over the two operands, returning - /// an `AllocatedBit`. - pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and result", - || { - if *a.value.get()? & *b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a) * (b) = (c), ensuring c is 1 iff - // a AND b are both 1. - cs.enforce( - || "and constraint", - |lc| lc + a.variable, - |lc| lc + b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `a AND (NOT b)`. - pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and not result", - || { - if *a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff - // a is true and b is false, and otherwise c is 0. - cs.enforce( - || "and not constraint", - |lc| lc + a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `(NOT a) AND (NOT b)`. - pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "nor result", - || { - if !*a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff - // a and b are both false, and otherwise c is 0. - cs.enforce( - || "nor constraint", - |lc| lc + CS::one() - a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } -} - -pub fn u64_into_boolean_vec_le>( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - let values = match value { - Some(ref value) => { - let mut tmp = Vec::with_capacity(64); - - for i in 0..64 { - tmp.push(Some(*value >> i & 1 == 1)); - } - - tmp - } - None => vec![None; 64], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, b)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("bit {}", i)), - b, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -pub fn field_into_boolean_vec_le< - Scalar: PrimeField, - CS: ConstraintSystem, - F: PrimeField, ->( - cs: CS, - value: Option, -) -> Result, SynthesisError> { - let v = field_into_allocated_bits_le::(cs, value)?; - - Ok(v.into_iter().map(Boolean::from).collect()) -} - -pub fn field_into_allocated_bits_le< - Scalar: PrimeField, - CS: ConstraintSystem, - F: PrimeField, ->( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - // Deconstruct in big-endian bit order - let values = match value { - Some(ref value) => { - let mut field_char = BitIterator::::new(F::char()); - - let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); - - let mut found_one = false; - for b in BitIterator::::new(value.to_repr()) { - // Skip leading bits - found_one |= field_char.next().unwrap(); - if !found_one { - continue; - } - - tmp.push(Some(b)); - } - - assert_eq!(tmp.len(), F::NUM_BITS as usize); - - tmp - } - None => vec![None; F::NUM_BITS as usize], - }; - - // Allocate in little-endian order - let bits = values - .into_iter() - .rev() - .enumerate() - .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), b)) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -/// This is a boolean value which may be either a constant or -/// an interpretation of an `AllocatedBit`. -#[derive(Clone)] -pub enum Boolean { - /// Existential view of the boolean variable - Is(AllocatedBit), - /// Negated view of the boolean variable - Not(AllocatedBit), - /// Constant (not an allocated variable) - Constant(bool), -} - -impl Boolean { - pub fn is_constant(&self) -> bool { - match *self { - Boolean::Constant(_) => true, - _ => false, - } - } - - pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(a), &Boolean::Constant(b)) => { - if a == b { - Ok(()) - } else { - Err(SynthesisError::Unsatisfiable) - } - } - (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => { - cs.enforce( - || "enforce equal to one", - |lc| lc, - |lc| lc, - |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => { - cs.enforce( - || "enforce equal to zero", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - (a, b) => { - cs.enforce( - || "enforce equal", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), Scalar::one()) - &b.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - } - } - - pub fn get_value(&self) -> Option { - match *self { - Boolean::Constant(c) => Some(c), - Boolean::Is(ref v) => v.get_value(), - Boolean::Not(ref v) => v.get_value().map(|b| !b), - } - } - - pub fn lc( - &self, - one: Variable, - coeff: Scalar, - ) -> LinearCombination { - match *self { - Boolean::Constant(c) => { - if c { - LinearCombination::::zero() + (coeff, one) - } else { - LinearCombination::::zero() - } - } - Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), - Boolean::Not(ref v) => { - LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) - } - } - } - - /// Construct a boolean from a known constant - pub fn constant(b: bool) -> Self { - Boolean::Constant(b) - } - - /// Return a negated interpretation of this boolean. - pub fn not(&self) -> Self { - match *self { - Boolean::Constant(c) => Boolean::Constant(!c), - Boolean::Is(ref v) => Boolean::Not(v.clone()), - Boolean::Not(ref v) => Boolean::Is(v.clone()), - } - } - - /// Perform XOR over two boolean operands - pub fn xor<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()), - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()), - // a XOR (NOT b) = NOT(a XOR b) - (is @ &Boolean::Is(_), not @ &Boolean::Not(_)) - | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => { - Ok(Boolean::xor(cs, is, ¬.not())?.not()) - } - // a XOR b = (NOT a) XOR (NOT b) - (&Boolean::Is(ref a), &Boolean::Is(ref b)) - | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?)) - } - } - } - - /// Perform AND over two boolean operands - pub fn and<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - // false AND x is always false - (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => { - Ok(Boolean::Constant(false)) - } - // true AND x is always x - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()), - // a AND (NOT b) - (&Boolean::Is(ref is), &Boolean::Not(ref not)) - | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => { - Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?)) - } - // (NOT a) AND (NOT b) = a NOR b - (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)) - } - // a AND b - (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { - Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)) - } - } - } - - /// Computes (a and b) xor ((not a) and c) - pub fn sha256_ch<'a, Scalar, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor ((not a) and c) - Some((a & b) ^ ((!a) & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(ch_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), _, c) => { - // If a is false - // (a and b) xor ((not a) and c) - // equals - // (false) xor (c) - // equals - // c - return Ok(c.clone()); - } - (a, &Boolean::Constant(false), c) => { - // If b is false - // (a and b) xor ((not a) and c) - // equals - // ((not a) and c) - return Boolean::and(cs, &a.not(), &c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false - // (a and b) xor ((not a) and c) - // equals - // (a and b) - return Boolean::and(cs, &a, &b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true - // (a and b) xor ((not a) and c) - // equals - // (a and b) xor (not a) - // equals - // not (a and (not b)) - return Ok(Boolean::and(cs, &a, &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true - // (a and b) xor ((not a) and c) - // equals - // a xor ((not a) and c) - // equals - // not ((not a) and (not c)) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), _, _) => { - // If a is true - // (a and b) xor ((not a) and c) - // equals - // b xor ((not a) and c) - // So we just continue! - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let ch = cs.alloc( - || "ch", - || { - ch_value - .get() - .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) - }, - )?; - - // a(b - c) = ch - c - cs.enforce( - || "ch computation", - |_| b.lc(CS::one(), Scalar::one()) - &c.lc(CS::one(), Scalar::one()), - |_| a.lc(CS::one(), Scalar::one()), - |lc| lc + ch - &c.lc(CS::one(), Scalar::one()), - ); - - Ok(AllocatedBit { - value: ch_value, - variable: ch, - } - .into()) - } - - /// Computes (a and b) xor (a and c) xor (b and c) - pub fn sha256_maj<'a, Scalar, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor (a and c) xor (b and c) - Some((a & b) ^ (a & c) ^ (b & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(maj_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), b, c) => { - // If a is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b and c) - return Boolean::and(cs, b, c); - } - (a, &Boolean::Constant(false), c) => { - // If b is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and c) - return Boolean::and(cs, a, c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) - return Boolean::and(cs, a, b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) xor (a) xor (b) - // equals - // not ((not a) and (not b)) - return Ok(Boolean::and(cs, &a.not(), &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a) xor (a and c) xor (c) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), b, c) => { - // If a is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b) xor (c) xor (b and c) - return Ok(Boolean::and(cs, &b.not(), &c.not())?.not()); - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let maj = cs.alloc( - || "maj", - || { - maj_value - .get() - .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) - }, - )?; - - // ¬(¬a ∧ ¬b) ∧ ¬(¬a ∧ ¬c) ∧ ¬(¬b ∧ ¬c) - // (1 - ((1 - a) * (1 - b))) * (1 - ((1 - a) * (1 - c))) * (1 - ((1 - b) * (1 - c))) - // (a + b - ab) * (a + c - ac) * (b + c - bc) - // -2abc + ab + ac + bc - // a (-2bc + b + c) + bc - // - // (b) * (c) = (bc) - // (2bc - b - c) * (a) = bc - maj - - let bc = Self::and(cs.namespace(|| "b and c"), b, c)?; - - cs.enforce( - || "maj computation", - |_| { - bc.lc(CS::one(), Scalar::one()) + &bc.lc(CS::one(), Scalar::one()) - - &b.lc(CS::one(), Scalar::one()) - - &c.lc(CS::one(), Scalar::one()) - }, - |_| a.lc(CS::one(), Scalar::one()), - |_| bc.lc(CS::one(), Scalar::one()) - maj, - ); - - Ok(AllocatedBit { - value: maj_value, - variable: maj, - } - .into()) - } -} - -impl From for Boolean { - fn from(b: AllocatedBit) -> Boolean { - Boolean::Is(b) - } -} - -#[cfg(test)] -mod test { - use super::{field_into_allocated_bits_le, u64_into_boolean_vec_le, AllocatedBit, Boolean}; - use crate::gadgets::test::*; - use crate::ConstraintSystem; - use bls12_381::Scalar; - use ff::{Field, PrimeField}; - - #[test] - fn test_allocated_bit() { - let mut cs = TestConstraintSystem::new(); - - AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - assert!(cs.get("boolean") == Scalar::one()); - assert!(cs.is_satisfied()); - cs.set("boolean", Scalar::zero()); - assert!(cs.is_satisfied()); - cs.set("boolean", Scalar::from_str("2").unwrap()); - assert!(!cs.is_satisfied()); - assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); - } - - #[test] - fn test_xor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val ^ *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("xor result") - == if *a_val ^ *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "xor result", - if *a_val ^ *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and result") - == if *a_val & *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and result", - if *a_val & *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and_not() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and not result") - == if *a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and not result", - if *a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_nor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), !*a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("nor result") - == if !*a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "nor result", - if !*a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_enforce_equal() { - for a_bool in [false, true].iter().cloned() { - for b_bool in [false, true].iter().cloned() { - for a_neg in [false, true].iter().cloned() { - for b_neg in [false, true].iter().cloned() { - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - let result = Boolean::enforce_equal(&mut cs, &a, &b); - - if (a_bool ^ a_neg) == (b_bool ^ b_neg) { - assert!(result.is_ok()); - assert!(cs.is_satisfied()); - } else { - assert!(result.is_err()); - } - } - } - } - } - } - } - - #[test] - fn test_boolean_negation() { - let mut cs = TestConstraintSystem::::new(); - - let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Not(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = Boolean::constant(true); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(false) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - } - - #[derive(Copy, Clone, Debug)] - enum OperandType { - True, - False, - AllocatedTrue, - AllocatedFalse, - NegatedAllocatedTrue, - NegatedAllocatedFalse, - } - - impl OperandType { - fn is_constant(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => true, - OperandType::AllocatedTrue => false, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => false, - } - } - - fn val(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => false, - OperandType::AllocatedTrue => true, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => true, - } - } - } - - #[test] - fn test_boolean_xor() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::xor(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Is(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - _ => panic!("this should never be encountered"), - } - } - } - } - - #[test] - fn test_boolean_and() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::and(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Constant(false)) => { - } - ( - OperandType::False, - OperandType::NegatedAllocatedTrue, - Boolean::Constant(false), - ) => {} - ( - OperandType::False, - OperandType::NegatedAllocatedFalse, - Boolean::Constant(false), - ) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Constant(false)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Constant(false)) => { - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - _ => { - panic!( - "unexpected behavior at {:?} AND {:?}", - first_operand, second_operand - ); - } - } - } - } - } - - #[test] - fn test_u64_into_boolean_vec_le() { - let mut cs = TestConstraintSystem::::new(); - - let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 64); - - assert_eq!(bits[63 - 0].get_value().unwrap(), true); - assert_eq!(bits[63 - 1].get_value().unwrap(), true); - assert_eq!(bits[63 - 2].get_value().unwrap(), true); - assert_eq!(bits[63 - 3].get_value().unwrap(), false); - assert_eq!(bits[63 - 4].get_value().unwrap(), true); - assert_eq!(bits[63 - 5].get_value().unwrap(), true); - assert_eq!(bits[63 - 20].get_value().unwrap(), true); - assert_eq!(bits[63 - 21].get_value().unwrap(), false); - assert_eq!(bits[63 - 22].get_value().unwrap(), false); - } - - #[test] - fn test_field_into_allocated_bits_le() { - let mut cs = TestConstraintSystem::::new(); - - let r = Scalar::from_str( - "9147677615426976802526883532204139322118074541891858454835346926874644257775", - ) - .unwrap(); - - let bits = field_into_allocated_bits_le(&mut cs, Some(r)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 255); - - assert_eq!(bits[254 - 0].value.unwrap(), false); - assert_eq!(bits[254 - 1].value.unwrap(), false); - assert_eq!(bits[254 - 2].value.unwrap(), true); - assert_eq!(bits[254 - 3].value.unwrap(), false); - assert_eq!(bits[254 - 4].value.unwrap(), true); - assert_eq!(bits[254 - 5].value.unwrap(), false); - assert_eq!(bits[254 - 20].value.unwrap(), true); - assert_eq!(bits[254 - 23].value.unwrap(), true); - } - - #[test] - fn test_boolean_sha256_ch() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::new(); - - let a; - let b; - let c; - - // ch = (a and b) xor ((not a) and c) - let expected = (first_operand.val() & second_operand.val()) - ^ ((!first_operand.val()) & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_ch(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("ch"), { - if expected { - Scalar::one() - } else { - Scalar::zero() - } - }); - cs.set("ch", { - if expected { - Scalar::zero() - } else { - Scalar::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); - } - } - } - } - } - - #[test] - fn test_boolean_sha256_maj() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::new(); - - let a; - let b; - let c; - - // maj = (a and b) xor (a and c) xor (b and c) - let expected = (first_operand.val() & second_operand.val()) - ^ (first_operand.val() & third_operand.val()) - ^ (second_operand.val() & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_maj(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("maj"), { - if expected { - Scalar::one() - } else { - Scalar::zero() - } - }); - cs.set("maj", { - if expected { - Scalar::zero() - } else { - Scalar::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "maj computation"); - } - } - } - } - } - - #[test] - fn test_alloc_conditionally() { - { - let mut cs = TestConstraintSystem::::new(); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - - let value = None; - // if value is none, fail with SynthesisError - let is_err = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .is_err(); - assert!(is_err); - } - - { - // since value is true, b must be false, so it should succeed - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - let allocated_value = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .unwrap(); - - assert_eq!(allocated_value.get_value().unwrap(), true); - assert!(cs.is_satisfied()); - } - - { - // since value is true, b must be false, so it should fail - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b) - .unwrap(); - - assert!(!cs.is_satisfied()); - } - - { - // since value is false, we don't care about the value of the bit - - let value = Some(false); - //check with false bit - let mut cs = TestConstraintSystem::::new(); - let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) - .unwrap(); - - assert!(cs.is_satisfied()); - - //check with true bit - let mut cs = TestConstraintSystem::::new(); - let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) - .unwrap(); - - assert!(cs.is_satisfied()); - } - } -} diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs deleted file mode 100644 index 2ef6f697c8..0000000000 --- a/bellman/src/gadgets/lookup.rs +++ /dev/null @@ -1,319 +0,0 @@ -//! Window table lookup gadgets. - -use ff::PrimeField; - -use super::boolean::Boolean; -use super::num::{AllocatedNum, Num}; -use super::*; -use crate::ConstraintSystem; - -// Synthesize the constants for each base pattern. -fn synth<'a, Scalar: PrimeField, I>(window_size: usize, constants: I, assignment: &mut [Scalar]) -where - I: IntoIterator, -{ - assert_eq!(assignment.len(), 1 << window_size); - - for (i, constant) in constants.into_iter().enumerate() { - let mut cur = assignment[i].neg(); - cur.add_assign(constant); - assignment[i] = cur; - for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) { - if j & i == i { - eval.add_assign(&cur); - } - } - } -} - -/// Performs a 3-bit window table lookup. `bits` is in -/// little-endian order. -pub fn lookup3_xy( - mut cs: CS, - bits: &[Boolean], - coords: &[(Scalar, Scalar)], -) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> -where - CS: ConstraintSystem, -{ - assert_eq!(bits.len(), 3); - assert_eq!(coords.len(), 8); - - // Calculate the index into `coords` - let i = match ( - bits[0].get_value(), - bits[1].get_value(), - bits[2].get_value(), - ) { - (Some(a_value), Some(b_value), Some(c_value)) => { - let mut tmp = 0; - if a_value { - tmp += 1; - } - if b_value { - tmp += 2; - } - if c_value { - tmp += 4; - } - Some(tmp) - } - _ => None, - }; - - // Allocate the x-coordinate resulting from the lookup - let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?; - - // Allocate the y-coordinate resulting from the lookup - let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?; - - // Compute the coefficients for the lookup constraints - let mut x_coeffs = [Scalar::zero(); 8]; - let mut y_coeffs = [Scalar::zero(); 8]; - synth::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(3, coords.iter().map(|c| &c.1), &mut y_coeffs); - - let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?; - - let one = CS::one(); - - cs.enforce( - || "x-coordinate lookup", - |lc| { - lc + (x_coeffs[0b001], one) - + &bits[1].lc::(one, x_coeffs[0b011]) - + &bits[2].lc::(one, x_coeffs[0b101]) - + &precomp.lc::(one, x_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, Scalar::one()), - |lc| { - lc + res_x.get_variable() - - (x_coeffs[0b000], one) - - &bits[1].lc::(one, x_coeffs[0b010]) - - &bits[2].lc::(one, x_coeffs[0b100]) - - &precomp.lc::(one, x_coeffs[0b110]) - }, - ); - - cs.enforce( - || "y-coordinate lookup", - |lc| { - lc + (y_coeffs[0b001], one) - + &bits[1].lc::(one, y_coeffs[0b011]) - + &bits[2].lc::(one, y_coeffs[0b101]) - + &precomp.lc::(one, y_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, Scalar::one()), - |lc| { - lc + res_y.get_variable() - - (y_coeffs[0b000], one) - - &bits[1].lc::(one, y_coeffs[0b010]) - - &bits[2].lc::(one, y_coeffs[0b100]) - - &precomp.lc::(one, y_coeffs[0b110]) - }, - ); - - Ok((res_x, res_y)) -} - -/// Performs a 3-bit window table lookup, where -/// one of the bits is a sign bit. -pub fn lookup3_xy_with_conditional_negation( - mut cs: CS, - bits: &[Boolean], - coords: &[(Scalar, Scalar)], -) -> Result<(Num, Num), SynthesisError> -where - CS: ConstraintSystem, -{ - assert_eq!(bits.len(), 3); - assert_eq!(coords.len(), 4); - - // Calculate the index into `coords` - let i = match (bits[0].get_value(), bits[1].get_value()) { - (Some(a_value), Some(b_value)) => { - let mut tmp = 0; - if a_value { - tmp += 1; - } - if b_value { - tmp += 2; - } - Some(tmp) - } - _ => None, - }; - - // Allocate the y-coordinate resulting from the lookup - // and conditional negation - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { - let mut tmp = coords[*i.get()?].1; - if *bits[2].get_value().get()? { - tmp = tmp.neg(); - } - Ok(tmp) - })?; - - let one = CS::one(); - - // Compute the coefficients for the lookup constraints - let mut x_coeffs = [Scalar::zero(); 4]; - let mut y_coeffs = [Scalar::zero(); 4]; - synth::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(2, coords.iter().map(|c| &c.1), &mut y_coeffs); - - let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?; - - let x = Num::zero() - .add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00]) - .add_bool_with_coeff(one, &bits[0], x_coeffs[0b01]) - .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) - .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); - - let y_lc = precomp.lc::(one, y_coeffs[0b11]) - + &bits[1].lc::(one, y_coeffs[0b10]) - + &bits[0].lc::(one, y_coeffs[0b01]) - + (y_coeffs[0b00], one); - - cs.enforce( - || "y-coordinate lookup", - |lc| lc + &y_lc + &y_lc, - |lc| lc + &bits[2].lc::(one, Scalar::one()), - |lc| lc + &y_lc - y.get_variable(), - ); - - Ok((x, y.into())) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::gadgets::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::*; - - use bls12_381::Scalar; - use ff::Field; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - use std::ops::{AddAssign, Neg}; - - #[test] - fn test_lookup3_xy() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..100 { - let mut cs = TestConstraintSystem::new(); - - let a_val = rng.next_u32() % 2 != 0; - let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); - - let b_val = rng.next_u32() % 2 != 0; - let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); - - let c_val = rng.next_u32() % 2 != 0; - let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); - - let bits = vec![a, b, c]; - - let points: Vec<(Scalar, Scalar)> = (0..8) - .map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng))) - .collect(); - - let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); - - assert!(cs.is_satisfied()); - - let mut index = 0; - if a_val { - index += 1 - } - if b_val { - index += 2 - } - if c_val { - index += 4 - } - - assert_eq!(res.0.get_value().unwrap(), points[index].0); - assert_eq!(res.1.get_value().unwrap(), points[index].1); - } - } - - #[test] - fn test_lookup3_xy_with_conditional_negation() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..100 { - let mut cs = TestConstraintSystem::new(); - - let a_val = rng.next_u32() % 2 != 0; - let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); - - let b_val = rng.next_u32() % 2 != 0; - let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); - - let c_val = rng.next_u32() % 2 != 0; - let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); - - let bits = vec![a, b, c]; - - let points: Vec<(Scalar, Scalar)> = (0..4) - .map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng))) - .collect(); - - let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); - - assert!(cs.is_satisfied()); - - let mut index = 0; - if a_val { - index += 1 - } - if b_val { - index += 2 - } - - assert_eq!(res.0.get_value().unwrap(), points[index].0); - let mut tmp = points[index].1; - if c_val { - tmp = tmp.neg() - } - assert_eq!(res.1.get_value().unwrap(), tmp); - } - } - - #[test] - fn test_synth() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let window_size = 4; - - let mut assignment = vec![Scalar::zero(); 1 << window_size]; - let constants: Vec<_> = (0..(1 << window_size)) - .map(|_| Scalar::random(&mut rng)) - .collect(); - - synth(window_size, &constants, &mut assignment); - - for b in 0..(1 << window_size) { - let mut acc = Scalar::zero(); - - for j in 0..(1 << window_size) { - if j & b == j { - acc.add_assign(&assignment[j]); - } - } - - assert_eq!(acc, constants[b]); - } - } -} diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs deleted file mode 100644 index b2bc151cb9..0000000000 --- a/bellman/src/gadgets/multieq.rs +++ /dev/null @@ -1,123 +0,0 @@ -use ff::PrimeField; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -pub struct MultiEq> { - cs: CS, - ops: usize, - bits_used: usize, - lhs: LinearCombination, - rhs: LinearCombination, -} - -impl> MultiEq { - pub fn new(cs: CS) -> Self { - MultiEq { - cs, - ops: 0, - bits_used: 0, - lhs: LinearCombination::zero(), - rhs: LinearCombination::zero(), - } - } - - fn accumulate(&mut self) { - let ops = self.ops; - let lhs = self.lhs.clone(); - let rhs = self.rhs.clone(); - self.cs.enforce( - || format!("multieq {}", ops), - |_| lhs, - |lc| lc + CS::one(), - |_| rhs, - ); - self.lhs = LinearCombination::zero(); - self.rhs = LinearCombination::zero(); - self.bits_used = 0; - self.ops += 1; - } - - pub fn enforce_equal( - &mut self, - num_bits: usize, - lhs: &LinearCombination, - rhs: &LinearCombination, - ) { - // Check if we will exceed the capacity - if (Scalar::CAPACITY as usize) <= (self.bits_used + num_bits) { - self.accumulate(); - } - - assert!((Scalar::CAPACITY as usize) > (self.bits_used + num_bits)); - - let coeff = Scalar::from_str("2") - .unwrap() - .pow_vartime(&[self.bits_used as u64]); - self.lhs = self.lhs.clone() + (coeff, lhs); - self.rhs = self.rhs.clone() + (coeff, rhs); - self.bits_used += num_bits; - } -} - -impl> Drop for MultiEq { - fn drop(&mut self) { - if self.bits_used > 0 { - self.accumulate(); - } - } -} - -impl> ConstraintSystem - for MultiEq -{ - type Root = Self; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - self.cs.enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR, - { - self.cs.get_root().push_namespace(name_fn) - } - - fn pop_namespace(&mut self) { - self.cs.get_root().pop_namespace() - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } -} diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs deleted file mode 100644 index fda716e679..0000000000 --- a/bellman/src/gadgets/multipack.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Helpers for packing vectors of bits into scalar field elements. - -use super::boolean::Boolean; -use super::num::Num; -use super::Assignment; -use crate::{ConstraintSystem, SynthesisError}; -use ff::PrimeField; - -/// Takes a sequence of booleans and exposes them as compact -/// public inputs -pub fn pack_into_inputs(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - for (i, bits) in bits.chunks(Scalar::CAPACITY as usize).enumerate() { - let mut num = Num::::zero(); - let mut coeff = Scalar::one(); - for bit in bits { - num = num.add_bool_with_coeff(CS::one(), bit, coeff); - - coeff = coeff.double(); - } - - let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?; - - // num * 1 = input - cs.enforce( - || format!("packing constraint {}", i), - |_| num.lc(Scalar::one()), - |lc| lc + CS::one(), - |lc| lc + input, - ); - } - - Ok(()) -} - -pub fn bytes_to_bits(bytes: &[u8]) -> Vec { - bytes - .iter() - .flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1)) - .collect() -} - -pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec { - bytes - .iter() - .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) - .collect() -} - -pub fn compute_multipacking(bits: &[bool]) -> Vec { - let mut result = vec![]; - - for bits in bits.chunks(Scalar::CAPACITY as usize) { - let mut cur = Scalar::zero(); - let mut coeff = Scalar::one(); - - for bit in bits { - if *bit { - cur.add_assign(&coeff); - } - - coeff = coeff.double(); - } - - result.push(cur); - } - - result -} - -#[test] -fn test_multipacking() { - use crate::ConstraintSystem; - use bls12_381::Scalar; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - use super::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::*; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for num_bits in 0..1500 { - let mut cs = TestConstraintSystem::::new(); - - let bits: Vec = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect(); - - let circuit_bits = bits - .iter() - .enumerate() - .map(|(i, &b)| { - Boolean::from( - AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(), - ) - }) - .collect::>(); - - let expected_inputs = compute_multipacking(&bits); - - pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap(); - - assert!(cs.is_satisfied()); - assert!(cs.verify(&expected_inputs)); - } -} diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs deleted file mode 100644 index f0267579d3..0000000000 --- a/bellman/src/gadgets/num.rs +++ /dev/null @@ -1,591 +0,0 @@ -//! Gadgets representing numbers in the scalar field of the underlying curve. - -use ff::{BitIterator, PrimeField}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use super::Assignment; - -use super::boolean::{self, AllocatedBit, Boolean}; - -pub struct AllocatedNum { - value: Option, - variable: Variable, -} - -impl Clone for AllocatedNum { - fn clone(&self) -> Self { - AllocatedNum { - value: self.value, - variable: self.variable, - } - } -} - -impl AllocatedNum { - pub fn alloc(mut cs: CS, value: F) -> Result - where - CS: ConstraintSystem, - F: FnOnce() -> Result, - { - let mut new_value = None; - let var = cs.alloc( - || "num", - || { - let tmp = value()?; - - new_value = Some(tmp); - - Ok(tmp) - }, - )?; - - Ok(AllocatedNum { - value: new_value, - variable: var, - }) - } - - pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?; - - cs.enforce( - || "enforce input is correct", - |lc| lc + input, - |lc| lc + CS::one(), - |lc| lc + self.variable, - ); - - Ok(()) - } - - /// Deconstructs this allocated number into its - /// boolean representation in little-endian bit - /// order, requiring that the representation - /// strictly exists "in the field" (i.e., a - /// congruency is not allowed.) - pub fn to_bits_le_strict(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - pub fn kary_and( - mut cs: CS, - v: &[AllocatedBit], - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - assert!(!v.is_empty()); - - // Let's keep this simple for now and just AND them all - // manually - let mut cur = None; - - for (i, v) in v.iter().enumerate() { - if cur.is_none() { - cur = Some(v.clone()); - } else { - cur = Some(AllocatedBit::and( - cs.namespace(|| format!("and {}", i)), - cur.as_ref().unwrap(), - v, - )?); - } - } - - Ok(cur.expect("v.len() > 0")) - } - - // We want to ensure that the bit representation of a is - // less than or equal to r - 1. - let mut a = self.value.map(|e| BitIterator::::new(e.to_repr())); - let b = (-Scalar::one()).to_repr(); - - let mut result = vec![]; - - // Runs of ones in r - let mut last_run = None; - let mut current_run = vec![]; - - let mut found_one = false; - let mut i = 0; - for b in BitIterator::::new(b) { - let a_bit = a.as_mut().map(|e| e.next().unwrap()); - - // Skip over unset bits at the beginning - found_one |= b; - if !found_one { - // a_bit should also be false - a_bit.map(|e| assert!(!e)); - continue; - } - - if b { - // This is part of a run of ones. Let's just - // allocate the boolean with the expected value. - let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?; - // ... and add it to the current run of ones. - current_run.push(a_bit.clone()); - result.push(a_bit); - } else { - if !current_run.is_empty() { - // This is the start of a run of zeros, but we need - // to k-ary AND against `last_run` first. - - if last_run.is_some() { - current_run.push(last_run.clone().unwrap()); - } - last_run = Some(kary_and( - cs.namespace(|| format!("run ending at {}", i)), - ¤t_run, - )?); - current_run.truncate(0); - } - - // If `last_run` is true, `a` must be false, or it would - // not be in the field. - // - // If `last_run` is false, `a` can be true or false. - - let a_bit = AllocatedBit::alloc_conditionally( - cs.namespace(|| format!("bit {}", i)), - a_bit, - &last_run.as_ref().expect("char always starts with a one"), - )?; - result.push(a_bit); - } - - i += 1; - } - - // char is prime, so we'll always end on - // a run of zeros. - assert_eq!(current_run.len(), 0); - - // Now, we have `result` in big-endian order. - // However, now we have to unpack self! - - let mut lc = LinearCombination::zero(); - let mut coeff = Scalar::one(); - - for bit in result.iter().rev() { - lc = lc + (coeff, bit.get_variable()); - - coeff = coeff.double(); - } - - lc = lc - self.variable; - - cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); - - // Convert into booleans, and reverse for little-endian bit order - Ok(result.into_iter().map(Boolean::from).rev().collect()) - } - - /// Convert the allocated number into its little-endian representation. - /// Note that this does not strongly enforce that the commitment is - /// "in the field." - pub fn to_bits_le(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?; - - let mut lc = LinearCombination::zero(); - let mut coeff = Scalar::one(); - - for bit in bits.iter() { - lc = lc + (coeff, bit.get_variable()); - - coeff = coeff.double(); - } - - lc = lc - self.variable; - - cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); - - Ok(bits.into_iter().map(Boolean::from).collect()) - } - - pub fn mul(&self, mut cs: CS, other: &Self) -> Result - where - CS: ConstraintSystem, - { - let mut value = None; - - let var = cs.alloc( - || "product num", - || { - let mut tmp = *self.value.get()?; - tmp.mul_assign(other.value.get()?); - - value = Some(tmp); - - Ok(tmp) - }, - )?; - - // Constrain: a * b = ab - cs.enforce( - || "multiplication constraint", - |lc| lc + self.variable, - |lc| lc + other.variable, - |lc| lc + var, - ); - - Ok(AllocatedNum { - value, - variable: var, - }) - } - - pub fn square(&self, mut cs: CS) -> Result - where - CS: ConstraintSystem, - { - let mut value = None; - - let var = cs.alloc( - || "squared num", - || { - let tmp = self.value.get()?.square(); - - value = Some(tmp); - - Ok(tmp) - }, - )?; - - // Constrain: a * a = aa - cs.enforce( - || "squaring constraint", - |lc| lc + self.variable, - |lc| lc + self.variable, - |lc| lc + var, - ); - - Ok(AllocatedNum { - value, - variable: var, - }) - } - - pub fn assert_nonzero(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - let inv = cs.alloc( - || "ephemeral inverse", - || { - let tmp = *self.value.get()?; - - if tmp.is_zero() { - Err(SynthesisError::DivisionByZero) - } else { - Ok(tmp.invert().unwrap()) - } - }, - )?; - - // Constrain a * inv = 1, which is only valid - // iff a has a multiplicative inverse, untrue - // for zero. - cs.enforce( - || "nonzero assertion constraint", - |lc| lc + self.variable, - |lc| lc + inv, - |lc| lc + CS::one(), - ); - - Ok(()) - } - - /// Takes two allocated numbers (a, b) and returns - /// (b, a) if the condition is true, and (a, b) - /// otherwise. - pub fn conditionally_reverse( - mut cs: CS, - a: &Self, - b: &Self, - condition: &Boolean, - ) -> Result<(Self, Self), SynthesisError> - where - CS: ConstraintSystem, - { - let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || { - if *condition.get_value().get()? { - Ok(*b.value.get()?) - } else { - Ok(*a.value.get()?) - } - })?; - - cs.enforce( - || "first conditional reversal", - |lc| lc + a.variable - b.variable, - |_| condition.lc(CS::one(), Scalar::one()), - |lc| lc + a.variable - c.variable, - ); - - let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || { - if *condition.get_value().get()? { - Ok(*a.value.get()?) - } else { - Ok(*b.value.get()?) - } - })?; - - cs.enforce( - || "second conditional reversal", - |lc| lc + b.variable - a.variable, - |_| condition.lc(CS::one(), Scalar::one()), - |lc| lc + b.variable - d.variable, - ); - - Ok((c, d)) - } - - pub fn get_value(&self) -> Option { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } -} - -pub struct Num { - value: Option, - lc: LinearCombination, -} - -impl From> for Num { - fn from(num: AllocatedNum) -> Num { - Num { - value: num.value, - lc: LinearCombination::::zero() + num.variable, - } - } -} - -impl Num { - pub fn zero() -> Self { - Num { - value: Some(Scalar::zero()), - lc: LinearCombination::zero(), - } - } - - pub fn get_value(&self) -> Option { - self.value - } - - pub fn lc(&self, coeff: Scalar) -> LinearCombination { - LinearCombination::zero() + (coeff, &self.lc) - } - - pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: Scalar) -> Self { - let newval = match (self.value, bit.get_value()) { - (Some(mut curval), Some(bval)) => { - if bval { - curval.add_assign(&coeff); - } - - Some(curval) - } - _ => None, - }; - - Num { - value: newval, - lc: self.lc + &bit.lc(one, coeff), - } - } -} - -#[cfg(test)] -mod test { - use crate::ConstraintSystem; - use bls12_381::Scalar; - use ff::{BitIterator, Field, PrimeField}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::{Neg, SubAssign}; - - use super::{AllocatedNum, Boolean}; - use crate::gadgets::test::*; - - #[test] - fn test_allocated_num() { - let mut cs = TestConstraintSystem::new(); - - AllocatedNum::alloc(&mut cs, || Ok(Scalar::one())).unwrap(); - - assert!(cs.get("num") == Scalar::one()); - } - - #[test] - fn test_num_squaring() { - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap(); - let n2 = n.square(&mut cs).unwrap(); - - assert!(cs.is_satisfied()); - assert!(cs.get("squared num") == Scalar::from_str("9").unwrap()); - assert!(n2.value.unwrap() == Scalar::from_str("9").unwrap()); - cs.set("squared num", Scalar::from_str("10").unwrap()); - assert!(!cs.is_satisfied()); - } - - #[test] - fn test_num_multiplication() { - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::from_str("12").unwrap())) - .unwrap(); - let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::from_str("10").unwrap())) - .unwrap(); - let n3 = n.mul(&mut cs, &n2).unwrap(); - - assert!(cs.is_satisfied()); - assert!(cs.get("product num") == Scalar::from_str("120").unwrap()); - assert!(n3.value.unwrap() == Scalar::from_str("120").unwrap()); - cs.set("product num", Scalar::from_str("121").unwrap()); - assert!(!cs.is_satisfied()); - } - - #[test] - fn test_num_conditional_reversal() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - { - let mut cs = TestConstraintSystem::new(); - - let a = - AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap(); - let b = - AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap(); - let condition = Boolean::constant(false); - let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(a.value.unwrap(), c.value.unwrap()); - assert_eq!(b.value.unwrap(), d.value.unwrap()); - } - - { - let mut cs = TestConstraintSystem::new(); - - let a = - AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Scalar::random(&mut rng))).unwrap(); - let b = - AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Scalar::random(&mut rng))).unwrap(); - let condition = Boolean::constant(true); - let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(a.value.unwrap(), d.value.unwrap()); - assert_eq!(b.value.unwrap(), c.value.unwrap()); - } - } - - #[test] - fn test_num_nonzero() { - { - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::from_str("3").unwrap())).unwrap(); - n.assert_nonzero(&mut cs).unwrap(); - - assert!(cs.is_satisfied()); - cs.set("ephemeral inverse", Scalar::from_str("3").unwrap()); - assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint")); - } - { - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(Scalar::zero())).unwrap(); - assert!(n.assert_nonzero(&mut cs).is_err()); - } - } - - #[test] - fn test_into_bits_strict() { - let negone = Scalar::one().neg(); - - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); - n.to_bits_le_strict(&mut cs).unwrap(); - - assert!(cs.is_satisfied()); - - // make the bit representation the characteristic - cs.set("bit 254/boolean", Scalar::one()); - - // this makes the conditional boolean constraint fail - assert_eq!( - cs.which_is_unsatisfied().unwrap(), - "bit 254/boolean constraint" - ); - } - - #[test] - fn test_into_bits() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for i in 0..200 { - let r = Scalar::random(&mut rng); - let mut cs = TestConstraintSystem::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); - - let bits = if i % 2 == 0 { - n.to_bits_le(&mut cs).unwrap() - } else { - n.to_bits_le_strict(&mut cs).unwrap() - }; - - assert!(cs.is_satisfied()); - - for (b, a) in BitIterator::::new(r.to_repr()) - .skip(1) - .zip(bits.iter().rev()) - { - if let &Boolean::Is(ref a) = a { - assert_eq!(b, a.get_value().unwrap()); - } else { - unreachable!() - } - } - - cs.set("num", Scalar::random(&mut rng)); - assert!(!cs.is_satisfied()); - cs.set("num", r); - assert!(cs.is_satisfied()); - - for i in 0..Scalar::NUM_BITS { - let name = format!("bit {}/boolean", i); - let cur = cs.get(&name); - let mut tmp = Scalar::one(); - tmp.sub_assign(&cur); - cs.set(&name, tmp); - assert!(!cs.is_satisfied()); - cs.set(&name, cur); - assert!(cs.is_satisfied()); - } - } - } -} diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs deleted file mode 100644 index 4ffc03254d..0000000000 --- a/bellman/src/gadgets/sha256.rs +++ /dev/null @@ -1,388 +0,0 @@ -//! Circuits for the [SHA-256] hash function and its internal compression -//! function. -//! -//! [SHA-256]: https://tools.ietf.org/html/rfc6234 - -use super::boolean::Boolean; -use super::multieq::MultiEq; -use super::uint32::UInt32; -use crate::{ConstraintSystem, SynthesisError}; -use ff::PrimeField; - -#[allow(clippy::unreadable_literal)] -const ROUND_CONSTANTS: [u32; 64] = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -]; - -#[allow(clippy::unreadable_literal)] -const IV: [u32; 8] = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, -]; - -pub fn sha256_block_no_padding( - mut cs: CS, - input: &[Boolean], -) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - - Ok( - sha256_compression_function(&mut cs, &input, &get_sha256_iv())? - .into_iter() - .flat_map(|e| e.into_bits_be()) - .collect(), - ) -} - -pub fn sha256(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - assert!(input.len() % 8 == 0); - - let mut padded = input.to_vec(); - let plen = padded.len() as u64; - // append a single '1' bit - padded.push(Boolean::constant(true)); - // append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512 - while (padded.len() + 64) % 512 != 0 { - padded.push(Boolean::constant(false)); - } - // append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits - for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) { - padded.push(Boolean::constant(b)); - } - assert!(padded.len() % 512 == 0); - - let mut cur = get_sha256_iv(); - for (i, block) in padded.chunks(512).enumerate() { - cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?; - } - - Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect()) -} - -fn get_sha256_iv() -> Vec { - IV.iter().map(|&v| UInt32::constant(v)).collect() -} - -fn sha256_compression_function( - cs: CS, - input: &[Boolean], - current_hash_value: &[UInt32], -) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - assert_eq!(current_hash_value.len(), 8); - - let mut w = input - .chunks(32) - .map(|e| UInt32::from_bits_be(e)) - .collect::>(); - - // We can save some constraints by combining some of - // the constraints in different u32 additions - let mut cs = MultiEq::new(cs); - - for i in 16..64 { - let cs = &mut cs.namespace(|| format!("w extension {}", i)); - - // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) - let mut s0 = w[i - 15].rotr(7); - s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?; - s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?; - - // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) - let mut s1 = w[i - 2].rotr(17); - s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?; - s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?; - - let tmp = UInt32::addmany( - cs.namespace(|| "computation of w[i]"), - &[w[i - 16].clone(), s0, w[i - 7].clone(), s1], - )?; - - // w[i] := w[i-16] + s0 + w[i-7] + s1 - w.push(tmp); - } - - assert_eq!(w.len(), 64); - - enum Maybe { - Deferred(Vec), - Concrete(UInt32), - } - - impl Maybe { - fn compute(self, cs: M, others: &[UInt32]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - Ok(match self { - Maybe::Concrete(ref v) => return Ok(v.clone()), - Maybe::Deferred(mut v) => { - v.extend(others.iter().cloned()); - UInt32::addmany(cs, &v)? - } - }) - } - } - - let mut a = Maybe::Concrete(current_hash_value[0].clone()); - let mut b = current_hash_value[1].clone(); - let mut c = current_hash_value[2].clone(); - let mut d = current_hash_value[3].clone(); - let mut e = Maybe::Concrete(current_hash_value[4].clone()); - let mut f = current_hash_value[5].clone(); - let mut g = current_hash_value[6].clone(); - let mut h = current_hash_value[7].clone(); - - for i in 0..64 { - let cs = &mut cs.namespace(|| format!("compression round {}", i)); - - // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) - let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; - let mut s1 = new_e.rotr(6); - s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?; - s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?; - - // ch := (e and f) xor ((not e) and g) - let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?; - - // temp1 := h + S1 + ch + k[i] + w[i] - let temp1 = vec![ - h.clone(), - s1, - ch, - UInt32::constant(ROUND_CONSTANTS[i]), - w[i].clone(), - ]; - - // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) - let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; - let mut s0 = new_a.rotr(2); - s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?; - s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?; - - // maj := (a and b) xor (a and c) xor (b and c) - let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?; - - // temp2 := S0 + maj - let temp2 = vec![s0, maj]; - - /* - h := g - g := f - f := e - e := d + temp1 - d := c - c := b - b := a - a := temp1 + temp2 - */ - - h = g; - g = f; - f = new_e; - e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::>()); - d = c; - c = b; - b = new_a; - a = Maybe::Deferred( - temp1 - .iter() - .cloned() - .chain(temp2.iter().cloned()) - .collect::>(), - ); - } - - /* - Add the compressed chunk to the current hash value: - h0 := h0 + a - h1 := h1 + b - h2 := h2 + c - h3 := h3 + d - h4 := h4 + e - h5 := h5 + f - h6 := h6 + g - h7 := h7 + h - */ - - let h0 = a.compute( - cs.namespace(|| "deferred h0 computation"), - &[current_hash_value[0].clone()], - )?; - - let h1 = UInt32::addmany( - cs.namespace(|| "new h1"), - &[current_hash_value[1].clone(), b], - )?; - - let h2 = UInt32::addmany( - cs.namespace(|| "new h2"), - &[current_hash_value[2].clone(), c], - )?; - - let h3 = UInt32::addmany( - cs.namespace(|| "new h3"), - &[current_hash_value[3].clone(), d], - )?; - - let h4 = e.compute( - cs.namespace(|| "deferred h4 computation"), - &[current_hash_value[4].clone()], - )?; - - let h5 = UInt32::addmany( - cs.namespace(|| "new h5"), - &[current_hash_value[5].clone(), f], - )?; - - let h6 = UInt32::addmany( - cs.namespace(|| "new h6"), - &[current_hash_value[6].clone(), g], - )?; - - let h7 = UInt32::addmany( - cs.namespace(|| "new h7"), - &[current_hash_value[7].clone(), h], - )?; - - Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::gadgets::boolean::AllocatedBit; - use crate::gadgets::test::TestConstraintSystem; - use bls12_381::Scalar; - use hex_literal::hex; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_blank_hash() { - let iv = get_sha256_iv(); - - let mut cs = TestConstraintSystem::::new(); - let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); - input_bits[0] = Boolean::Constant(true); - let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap(); - let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); - - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - - let mut out = out_bits.into_iter(); - for b in expected.iter() { - for i in (0..8).rev() { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_full_block() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let iv = get_sha256_iv(); - - let mut cs = TestConstraintSystem::::new(); - let input_bits: Vec<_> = (0..512) - .map(|i| { - Boolean::from( - AllocatedBit::alloc( - cs.namespace(|| format!("input bit {}", i)), - Some(rng.next_u32() % 2 != 0), - ) - .unwrap(), - ) - }) - .collect(); - - sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap(); - - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints() - 512, 25840); - } - - #[test] - fn test_against_vectors() { - use sha2::{Digest, Sha256}; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { - let mut h = Sha256::new(); - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - h.update(&data); - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::new(); - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in (0..8).rev() { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = sha256(&mut cs, &input_bits).unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .iter() - .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); - - for b in r { - match b { - Boolean::Is(b) => { - assert!(s.next().unwrap() == b.get_value().unwrap()); - } - Boolean::Not(b) => { - assert!(s.next().unwrap() != b.get_value().unwrap()); - } - Boolean::Constant(b) => { - assert!(input_len == 0); - assert!(s.next().unwrap() == b); - } - } - } - } - } -} diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs deleted file mode 100644 index 08f5391eda..0000000000 --- a/bellman/src/gadgets/test/mod.rs +++ /dev/null @@ -1,463 +0,0 @@ -//! Helpers for testing circuit implementations. - -use ff::{Endianness, PrimeField}; - -use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use std::collections::HashMap; -use std::fmt::Write; - -use byteorder::{BigEndian, ByteOrder}; -use std::cmp::Ordering; -use std::collections::BTreeMap; - -use blake2s_simd::{Params as Blake2sParams, State as Blake2sState}; - -#[derive(Debug)] -enum NamedObject { - Constraint(usize), - Var(Variable), - Namespace, -} - -/// Constraint system for testing purposes. -pub struct TestConstraintSystem { - named_objects: HashMap, - current_namespace: Vec, - constraints: Vec<( - LinearCombination, - LinearCombination, - LinearCombination, - String, - )>, - inputs: Vec<(Scalar, String)>, - aux: Vec<(Scalar, String)>, -} - -#[derive(Clone, Copy)] -struct OrderedVariable(Variable); - -impl Eq for OrderedVariable {} -impl PartialEq for OrderedVariable { - fn eq(&self, other: &OrderedVariable) -> bool { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a == b, - (Index::Aux(ref a), Index::Aux(ref b)) => a == b, - _ => false, - } - } -} -impl PartialOrd for OrderedVariable { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for OrderedVariable { - fn cmp(&self, other: &Self) -> Ordering { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), - (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), - (Index::Input(_), Index::Aux(_)) => Ordering::Less, - (Index::Aux(_), Index::Input(_)) => Ordering::Greater, - } - } -} - -fn proc_lc(terms: &[(Variable, Scalar)]) -> BTreeMap { - let mut map = BTreeMap::new(); - for &(var, coeff) in terms { - map.entry(OrderedVariable(var)) - .or_insert_with(Scalar::zero) - .add_assign(&coeff); - } - - // Remove terms that have a zero coefficient to normalize - let mut to_remove = vec![]; - for (var, coeff) in map.iter() { - if coeff.is_zero() { - to_remove.push(var.clone()) - } - } - - for var in to_remove { - map.remove(&var); - } - - map -} - -fn hash_lc(terms: &[(Variable, Scalar)], h: &mut Blake2sState) { - let map = proc_lc::(terms); - - let mut buf = [0u8; 9 + 32]; - BigEndian::write_u64(&mut buf[0..8], map.len() as u64); - h.update(&buf[0..8]); - - for (var, coeff) in map { - match var.0.get_unchecked() { - Index::Input(i) => { - buf[0] = b'I'; - BigEndian::write_u64(&mut buf[1..9], i as u64); - } - Index::Aux(i) => { - buf[0] = b'A'; - BigEndian::write_u64(&mut buf[1..9], i as u64); - } - } - - let mut coeff_repr = coeff.to_repr(); - ::ReprEndianness::toggle_little_endian(&mut coeff_repr); - let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect(); - buf[9..].copy_from_slice(&coeff_be[..]); - - h.update(&buf); - } -} - -fn eval_lc( - terms: &[(Variable, Scalar)], - inputs: &[(Scalar, String)], - aux: &[(Scalar, String)], -) -> Scalar { - let mut acc = Scalar::zero(); - - for &(var, ref coeff) in terms { - let mut tmp = match var.get_unchecked() { - Index::Input(index) => inputs[index].0, - Index::Aux(index) => aux[index].0, - }; - - tmp.mul_assign(coeff); - acc.add_assign(&tmp); - } - - acc -} - -impl TestConstraintSystem { - pub fn new() -> TestConstraintSystem { - let mut map = HashMap::new(); - map.insert( - "ONE".into(), - NamedObject::Var(TestConstraintSystem::::one()), - ); - - TestConstraintSystem { - named_objects: map, - current_namespace: vec![], - constraints: vec![], - inputs: vec![(Scalar::one(), "ONE".into())], - aux: vec![], - } - } - - pub fn pretty_print(&self) -> String { - let mut s = String::new(); - - let negone = Scalar::one().neg(); - - let powers_of_two = (0..Scalar::NUM_BITS) - .map(|i| Scalar::from_str("2").unwrap().pow_vartime(&[u64::from(i)])) - .collect::>(); - - let pp = |s: &mut String, lc: &LinearCombination| { - write!(s, "(").unwrap(); - let mut is_first = true; - for (var, coeff) in proc_lc::(lc.as_ref()) { - if coeff == negone { - write!(s, " - ").unwrap(); - } else if !is_first { - write!(s, " + ").unwrap(); - } - is_first = false; - - if coeff != Scalar::one() && coeff != negone { - for (i, x) in powers_of_two.iter().enumerate() { - if x == &coeff { - write!(s, "2^{} . ", i).unwrap(); - break; - } - } - - write!(s, "{} . ", coeff).unwrap(); - } - - match var.0.get_unchecked() { - Index::Input(i) => { - write!(s, "`{}`", &self.inputs[i].1).unwrap(); - } - Index::Aux(i) => { - write!(s, "`{}`", &self.aux[i].1).unwrap(); - } - } - } - if is_first { - // Nothing was visited, print 0. - write!(s, "0").unwrap(); - } - write!(s, ")").unwrap(); - }; - - for &(ref a, ref b, ref c, ref name) in &self.constraints { - write!(&mut s, "\n").unwrap(); - - write!(&mut s, "{}: ", name).unwrap(); - pp(&mut s, a); - write!(&mut s, " * ").unwrap(); - pp(&mut s, b); - write!(&mut s, " = ").unwrap(); - pp(&mut s, c); - } - - write!(&mut s, "\n").unwrap(); - - s - } - - pub fn hash(&self) -> String { - let mut h = Blake2sParams::new().hash_length(32).to_state(); - { - let mut buf = [0u8; 24]; - - BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64); - BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64); - BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64); - h.update(&buf); - } - - for constraint in &self.constraints { - hash_lc::(constraint.0.as_ref(), &mut h); - hash_lc::(constraint.1.as_ref(), &mut h); - hash_lc::(constraint.2.as_ref(), &mut h); - } - - let mut s = String::new(); - for b in h.finalize().as_ref() { - s += &format!("{:02x}", b); - } - - s - } - - pub fn which_is_unsatisfied(&self) -> Option<&str> { - for &(ref a, ref b, ref c, ref path) in &self.constraints { - let mut a = eval_lc::(a.as_ref(), &self.inputs, &self.aux); - let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); - let c = eval_lc::(c.as_ref(), &self.inputs, &self.aux); - - a.mul_assign(&b); - - if a != c { - return Some(&*path); - } - } - - None - } - - pub fn is_satisfied(&self) -> bool { - self.which_is_unsatisfied().is_none() - } - - pub fn num_constraints(&self) -> usize { - self.constraints.len() - } - - pub fn set(&mut self, path: &str, to: Scalar) { - match self.named_objects.get(path) { - Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { - Index::Input(index) => self.inputs[index].0 = to, - Index::Aux(index) => self.aux[index].0 = to, - }, - Some(e) => panic!( - "tried to set path `{}` to value, but `{:?}` already exists there.", - path, e - ), - _ => panic!("no variable exists at path: {}", path), - } - } - - pub fn verify(&self, expected: &[Scalar]) -> bool { - assert_eq!(expected.len() + 1, self.inputs.len()); - - for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) { - if &a.0 != b { - return false; - } - } - - true - } - - pub fn num_inputs(&self) -> usize { - self.inputs.len() - } - - pub fn get_input(&mut self, index: usize, path: &str) -> Scalar { - let (assignment, name) = self.inputs[index].clone(); - - assert_eq!(path, name); - - assignment - } - - pub fn get(&mut self, path: &str) -> Scalar { - match self.named_objects.get(path) { - Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { - Index::Input(index) => self.inputs[index].0, - Index::Aux(index) => self.aux[index].0, - }, - Some(e) => panic!( - "tried to get value of path `{}`, but `{:?}` exists there (not a variable)", - path, e - ), - _ => panic!("no variable exists at path: {}", path), - } - } - - fn set_named_obj(&mut self, path: String, to: NamedObject) { - if self.named_objects.contains_key(&path) { - panic!("tried to create object at existing path: {}", path); - } - - self.named_objects.insert(path, to); - } -} - -fn compute_path(ns: &[String], this: String) -> String { - if this.chars().any(|a| a == '/') { - panic!("'/' is not allowed in names"); - } - - let mut name = String::new(); - - let mut needs_separation = false; - for ns in ns.iter().chain(Some(&this).into_iter()) { - if needs_separation { - name += "/"; - } - - name += ns; - needs_separation = true; - } - - name -} - -impl ConstraintSystem for TestConstraintSystem { - type Root = Self; - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - let index = self.aux.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.aux.push((f()?, path.clone())); - let var = Variable::new_unchecked(Index::Aux(index)); - self.set_named_obj(path, NamedObject::Var(var)); - - Ok(var) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - let index = self.inputs.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.inputs.push((f()?, path.clone())); - let var = Variable::new_unchecked(Index::Input(index)); - self.set_named_obj(path, NamedObject::Var(var)); - - Ok(var) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - let path = compute_path(&self.current_namespace, annotation().into()); - let index = self.constraints.len(); - self.set_named_obj(path.clone(), NamedObject::Constraint(index)); - - let a = a(LinearCombination::zero()); - let b = b(LinearCombination::zero()); - let c = c(LinearCombination::zero()); - - self.constraints.push((a, b, c, path)); - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR, - { - let name = name_fn().into(); - let path = compute_path(&self.current_namespace, name.clone()); - self.set_named_obj(path.clone(), NamedObject::Namespace); - self.current_namespace.push(name); - } - - fn pop_namespace(&mut self) { - assert!(self.current_namespace.pop().is_some()); - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } -} - -#[test] -fn test_cs() { - use bls12_381::Scalar; - use ff::PrimeField; - - let mut cs = TestConstraintSystem::new(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - let a = cs - .namespace(|| "a") - .alloc(|| "var", || Ok(Scalar::from_str("10").unwrap())) - .unwrap(); - let b = cs - .namespace(|| "b") - .alloc(|| "var", || Ok(Scalar::from_str("4").unwrap())) - .unwrap(); - let c = cs - .alloc(|| "product", || Ok(Scalar::from_str("40").unwrap())) - .unwrap(); - - cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 1); - - cs.set("a/var", Scalar::from_str("4").unwrap()); - - let one = TestConstraintSystem::::one(); - cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); - - assert!(!cs.is_satisfied()); - assert!(cs.which_is_unsatisfied() == Some("mult")); - - assert!(cs.get("product") == Scalar::from_str("40").unwrap()); - - cs.set("product", Scalar::from_str("16").unwrap()); - assert!(cs.is_satisfied()); - - { - let mut cs = cs.namespace(|| "test1"); - let mut cs = cs.namespace(|| "test2"); - cs.alloc(|| "hehe", || Ok(Scalar::one())).unwrap(); - } - - assert!(cs.get("test1/test2/hehe") == Scalar::one()); -} diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs deleted file mode 100644 index db5e2d506f..0000000000 --- a/bellman/src/gadgets/uint32.rs +++ /dev/null @@ -1,767 +0,0 @@ -//! Circuit representation of a [`u32`], with helpers for the [`sha256`] -//! gadgets. -//! -//! [`sha256`]: crate::gadgets::sha256 - -use ff::PrimeField; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError}; - -use super::boolean::{AllocatedBit, Boolean}; - -use super::multieq::MultiEq; - -/// Represents an interpretation of 32 `Boolean` objects as an -/// unsigned integer. -#[derive(Clone)] -pub struct UInt32 { - // Least significant bit first - bits: Vec, - value: Option, -} - -impl UInt32 { - /// Construct a constant `UInt32` from a `u32` - pub fn constant(value: u32) -> Self { - let mut bits = Vec::with_capacity(32); - - let mut tmp = value; - for _ in 0..32 { - if tmp & 1 == 1 { - bits.push(Boolean::constant(true)) - } else { - bits.push(Boolean::constant(false)) - } - - tmp >>= 1; - } - - UInt32 { - bits, - value: Some(value), - } - } - - /// Allocate a `UInt32` in the constraint system - pub fn alloc(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let values = match value { - Some(mut val) => { - let mut v = Vec::with_capacity(32); - - for _ in 0..32 { - v.push(Some(val & 1 == 1)); - val >>= 1; - } - - v - } - None => vec![None; 32], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, v)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("allocated bit {}", i)), - v, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(UInt32 { bits, value }) - } - - pub fn into_bits_be(self) -> Vec { - let mut ret = self.bits; - ret.reverse(); - ret - } - - pub fn from_bits_be(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let mut value = Some(0u32); - for b in bits { - value.as_mut().map(|v| *v <<= 1); - - match b.get_value() { - Some(true) => { - value.as_mut().map(|v| *v |= 1); - } - Some(false) => {} - None => { - value = None; - } - } - } - - UInt32 { - value, - bits: bits.iter().rev().cloned().collect(), - } - } - - /// Turns this `UInt32` into its little-endian byte order representation. - pub fn into_bits(self) -> Vec { - self.bits - } - - /// Converts a little-endian byte order representation of bits into a - /// `UInt32`. - pub fn from_bits(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let new_bits = bits.to_vec(); - - let mut value = Some(0u32); - for b in new_bits.iter().rev() { - value.as_mut().map(|v| *v <<= 1); - - match *b { - Boolean::Constant(b) => { - if b { - value.as_mut().map(|v| *v |= 1); - } - } - Boolean::Is(ref b) => match b.get_value() { - Some(true) => { - value.as_mut().map(|v| *v |= 1); - } - Some(false) => {} - None => value = None, - }, - Boolean::Not(ref b) => match b.get_value() { - Some(false) => { - value.as_mut().map(|v| *v |= 1); - } - Some(true) => {} - None => value = None, - }, - } - } - - UInt32 { - value, - bits: new_bits, - } - } - - pub fn rotr(&self, by: usize) -> Self { - let by = by % 32; - - let new_bits = self - .bits - .iter() - .skip(by) - .chain(self.bits.iter()) - .take(32) - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v.rotate_right(by as u32)), - } - } - - pub fn shr(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - - let new_bits = self - .bits - .iter() // The bits are least significant first - .skip(by) // Skip the bits that will be lost during the shift - .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v >> by as u32), - } - } - - fn triop( - mut cs: CS, - a: &Self, - b: &Self, - c: &Self, - tri_fn: F, - circuit_fn: U, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - F: Fn(u32, u32, u32) -> u32, - U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, - { - let new_value = match (a.value, b.value, c.value) { - (Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)), - _ => None, - }; - - let bits = a - .bits - .iter() - .zip(b.bits.iter()) - .zip(c.bits.iter()) - .enumerate() - .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Compute the `maj` value (a and b) xor (a and c) xor (b and c) - /// during SHA256. - pub fn sha256_maj( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) ^ (a & c) ^ (b & c), - |cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c), - ) - } - - /// Compute the `ch` value `(a and b) xor ((not a) and c)` - /// during SHA256. - pub fn sha256_ch( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) ^ ((!a) & c), - |cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c), - ) - } - - /// XOR this `UInt32` with another `UInt32` - pub fn xor(&self, mut cs: CS, other: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let new_value = match (self.value, other.value) { - (Some(a), Some(b)) => Some(a ^ b), - _ => None, - }; - - let bits = self - .bits - .iter() - .zip(other.bits.iter()) - .enumerate() - .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Perform modular addition of several `UInt32` objects. - pub fn addmany(mut cs: M, operands: &[Self]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - // Make some arbitrary bounds for ourselves to avoid overflows - // in the scalar field - assert!(Scalar::NUM_BITS >= 64); - assert!(operands.len() >= 2); // Weird trivial cases that should never happen - assert!(operands.len() <= 10); - - // Compute the maximum value of the sum so we allocate enough bits for - // the result - let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); - - // Keep track of the resulting value - let mut result_value = Some(0u64); - - // This is a linear combination that we will enforce to equal the - // output - let mut lc = LinearCombination::zero(); - - let mut all_constants = true; - - // Iterate over the operands - for op in operands { - // Accumulate the value - match op.value { - Some(val) => { - result_value.as_mut().map(|v| *v += u64::from(val)); - } - None => { - // If any of our operands have unknown value, we won't - // know the value of the result - result_value = None; - } - } - - // Iterate over each bit of the operand and add the operand to - // the linear combination - let mut coeff = Scalar::one(); - for bit in &op.bits { - lc = lc + &bit.lc(CS::one(), coeff); - - all_constants &= bit.is_constant(); - - coeff = coeff.double(); - } - } - - // The value of the actual result is modulo 2^32 - let modular_value = result_value.map(|v| v as u32); - - if all_constants && modular_value.is_some() { - // We can just return a constant, rather than - // unpacking the result into allocated bits. - - return Ok(UInt32::constant(modular_value.unwrap())); - } - - // Storage area for the resulting bits - let mut result_bits = vec![]; - - // Linear combination representing the output, - // for comparison with the sum of the operands - let mut result_lc = LinearCombination::zero(); - - // Allocate each bit of the result - let mut coeff = Scalar::one(); - let mut i = 0; - while max_value != 0 { - // Allocate the bit - let b = AllocatedBit::alloc( - cs.namespace(|| format!("result bit {}", i)), - result_value.map(|v| (v >> i) & 1 == 1), - )?; - - // Add this bit to the result combination - result_lc = result_lc + (coeff, b.get_variable()); - - result_bits.push(b.into()); - - max_value >>= 1; - i += 1; - coeff = coeff.double(); - } - - // Enforce equality between the sum and result - cs.get_root().enforce_equal(i, &lc, &result_lc); - - // Discard carry bits that we don't care about - result_bits.truncate(32); - - Ok(UInt32 { - bits: result_bits, - value: modular_value, - }) - } -} - -#[cfg(test)] -mod test { - use super::UInt32; - use crate::gadgets::boolean::Boolean; - use crate::gadgets::multieq::MultiEq; - use crate::gadgets::test::*; - use crate::ConstraintSystem; - use bls12_381::Scalar; - use ff::Field; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_uint32_from_bits_be() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits_be(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits_be(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_from_bits() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_xor() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a ^ b ^ c; - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); - let r = r.xor(cs.namespace(|| "second xor"), &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany_constants() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let a_bit = UInt32::constant(a); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - - let mut expected = a.wrapping_add(b).wrapping_add(c); - - let r = { - let mut cs = MultiEq::new(&mut cs); - let r = - UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); - r - }; - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(_) => panic!(), - Boolean::Not(_) => panic!(), - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - let d = rng.next_u32(); - - let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); - let r = { - let mut cs = MultiEq::new(&mut cs); - UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() - }; - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Constant(_) => unreachable!(), - } - - expected >>= 1; - } - - // Flip a bit and see if the addition constraint still works - if cs.get("addition/result bit 0/boolean").is_zero() { - cs.set("addition/result bit 0/boolean", Field::one()); - } else { - cs.set("addition/result bit 0/boolean", Field::zero()); - } - - assert!(!cs.is_satisfied()); - } - } - - #[test] - fn test_uint32_rotr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let mut num = rng.next_u32(); - - let a = UInt32::constant(num); - - for i in 0..32 { - let b = a.rotr(i); - assert_eq!(a.bits.len(), b.bits.len()); - - assert!(b.value.unwrap() == num); - - let mut tmp = num; - for b in &b.bits { - match *b { - Boolean::Constant(b) => { - assert_eq!(b, tmp & 1 == 1); - } - _ => unreachable!(), - } - - tmp >>= 1; - } - - num = num.rotate_right(1); - } - } - - #[test] - fn test_uint32_shr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shr(i); - let b = UInt32::constant(num.wrapping_shr(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_sha256_maj() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) ^ (a & c) ^ (b & c); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_sha256_ch() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) ^ ((!a) & c); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } -} diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs deleted file mode 100644 index b78efcec3d..0000000000 --- a/bellman/src/groth16/generator.rs +++ /dev/null @@ -1,501 +0,0 @@ -use rand_core::RngCore; -use std::ops::{AddAssign, MulAssign}; -use std::sync::Arc; - -use ff::{Field, PrimeField}; -use group::{prime::PrimeCurveAffine, Curve, Group, Wnaf, WnafGroup}; -use pairing::Engine; - -use super::{Parameters, VerifyingKey}; - -use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use crate::domain::{EvaluationDomain, Scalar}; - -use crate::multicore::Worker; - -/// Generates a random common reference string for -/// a circuit. -pub fn generate_random_parameters( - circuit: C, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - E::G1: WnafGroup, - E::G2: WnafGroup, - C: Circuit, - R: RngCore, -{ - let g1 = E::G1::random(rng); - let g2 = E::G2::random(rng); - let alpha = E::Fr::random(rng); - let beta = E::Fr::random(rng); - let gamma = E::Fr::random(rng); - let delta = E::Fr::random(rng); - let tau = E::Fr::random(rng); - - generate_parameters::(circuit, g1, g2, alpha, beta, gamma, delta, tau) -} - -/// This is our assembly structure that we'll use to synthesize the -/// circuit into a QAP. -struct KeypairAssembly { - num_inputs: usize, - num_aux: usize, - num_constraints: usize, - at_inputs: Vec>, - bt_inputs: Vec>, - ct_inputs: Vec>, - at_aux: Vec>, - bt_aux: Vec>, - ct_aux: Vec>, -} - -impl ConstraintSystem for KeypairAssembly { - type Root = Self; - - fn alloc(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // There is no assignment, so we don't even invoke the - // function for obtaining one. - - let index = self.num_aux; - self.num_aux += 1; - - self.at_aux.push(vec![]); - self.bt_aux.push(vec![]); - self.ct_aux.push(vec![]); - - Ok(Variable(Index::Aux(index))) - } - - fn alloc_input(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // There is no assignment, so we don't even invoke the - // function for obtaining one. - - let index = self.num_inputs; - self.num_inputs += 1; - - self.at_inputs.push(vec![]); - self.bt_inputs.push(vec![]); - self.ct_inputs.push(vec![]); - - Ok(Variable(Index::Input(index))) - } - - fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - fn eval( - l: LinearCombination, - inputs: &mut [Vec<(Scalar, usize)>], - aux: &mut [Vec<(Scalar, usize)>], - this_constraint: usize, - ) { - for (index, coeff) in l.0 { - match index { - Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), - Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)), - } - } - } - - eval( - a(LinearCombination::zero()), - &mut self.at_inputs, - &mut self.at_aux, - self.num_constraints, - ); - eval( - b(LinearCombination::zero()), - &mut self.bt_inputs, - &mut self.bt_aux, - self.num_constraints, - ); - eval( - c(LinearCombination::zero()), - &mut self.ct_inputs, - &mut self.ct_aux, - self.num_constraints, - ); - - self.num_constraints += 1; - } - - fn push_namespace(&mut self, _: N) - where - NR: Into, - N: FnOnce() -> NR, - { - // Do nothing; we don't care about namespaces in this context. - } - - fn pop_namespace(&mut self) { - // Do nothing; we don't care about namespaces in this context. - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } -} - -/// Create parameters for a circuit, given some toxic waste. -pub fn generate_parameters( - circuit: C, - g1: E::G1, - g2: E::G2, - alpha: E::Fr, - beta: E::Fr, - gamma: E::Fr, - delta: E::Fr, - tau: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - E::G1: WnafGroup, - E::G2: WnafGroup, - C: Circuit, -{ - let mut assembly = KeypairAssembly { - num_inputs: 0, - num_aux: 0, - num_constraints: 0, - at_inputs: vec![], - bt_inputs: vec![], - ct_inputs: vec![], - at_aux: vec![], - bt_aux: vec![], - ct_aux: vec![], - }; - - // Allocate the "one" input variable - assembly.alloc_input(|| "", || Ok(E::Fr::one()))?; - - // Synthesize the circuit. - circuit.synthesize(&mut assembly)?; - - // Input constraints to ensure full density of IC query - // x * 0 = 0 - for i in 0..assembly.num_inputs { - assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); - } - - // Create bases for blind evaluation of polynomials at tau - let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; - let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?; - - // Compute G1 window table - let mut g1_wnaf = Wnaf::new(); - let g1_wnaf = g1_wnaf.base(g1, { - // H query - (powers_of_tau.as_ref().len() - 1) - // IC/L queries - + assembly.num_inputs + assembly.num_aux - // A query - + assembly.num_inputs + assembly.num_aux - // B query - + assembly.num_inputs + assembly.num_aux - }); - - // Compute G2 window table - let mut g2_wnaf = Wnaf::new(); - let g2_wnaf = g2_wnaf.base(g2, { - // B query - assembly.num_inputs + assembly.num_aux - }); - - let gamma_inverse = { - let inverse = gamma.invert(); - if bool::from(inverse.is_some()) { - Ok(inverse.unwrap()) - } else { - Err(SynthesisError::UnexpectedIdentity) - } - }?; - let delta_inverse = { - let inverse = delta.invert(); - if bool::from(inverse.is_some()) { - Ok(inverse.unwrap()) - } else { - Err(SynthesisError::UnexpectedIdentity) - } - }?; - - let worker = Worker::new(); - - let mut h = vec![E::G1Affine::identity(); powers_of_tau.as_ref().len() - 1]; - { - // Compute powers of tau - { - let powers_of_tau = powers_of_tau.as_mut(); - worker.scope(powers_of_tau.len(), |scope, chunk| { - for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { - scope.spawn(move |_scope| { - let mut current_tau_power = tau.pow_vartime(&[(i * chunk) as u64]); - - for p in powers_of_tau { - p.0 = current_tau_power; - current_tau_power.mul_assign(&tau); - } - }); - } - }); - } - - // coeff = t(x) / delta - let mut coeff = powers_of_tau.z(&tau); - coeff.mul_assign(&delta_inverse); - - // Compute the H query with multiple threads - worker.scope(h.len(), |scope, chunk| { - for (h, p) in h - .chunks_mut(chunk) - .zip(powers_of_tau.as_ref().chunks(chunk)) - { - let mut g1_wnaf = g1_wnaf.shared(); - - scope.spawn(move |_scope| { - // Set values of the H query to g1^{(tau^i * t(tau)) / delta} - let h_proj: Vec<_> = p[..h.len()] - .iter() - .map(|p| { - // Compute final exponent - let mut exp = p.0; - exp.mul_assign(&coeff); - - // Exponentiate - g1_wnaf.scalar(&exp) - }) - .collect(); - - // Batch normalize - E::G1::batch_normalize(&h_proj, h); - }); - } - }); - } - - // Use inverse FFT to convert powers of tau to Lagrange coefficients - powers_of_tau.ifft(&worker); - let powers_of_tau = powers_of_tau.into_coeffs(); - - let mut a = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g1 = vec![E::G1Affine::identity(); assembly.num_inputs + assembly.num_aux]; - let mut b_g2 = vec![E::G2Affine::identity(); assembly.num_inputs + assembly.num_aux]; - let mut ic = vec![E::G1Affine::identity(); assembly.num_inputs]; - let mut l = vec![E::G1Affine::identity(); assembly.num_aux]; - - fn eval( - // wNAF window tables - g1_wnaf: &Wnaf>, - g2_wnaf: &Wnaf>, - - // Lagrange coefficients for tau - powers_of_tau: &[Scalar], - - // QAP polynomials - at: &[Vec<(E::Fr, usize)>], - bt: &[Vec<(E::Fr, usize)>], - ct: &[Vec<(E::Fr, usize)>], - - // Resulting evaluated QAP polynomials - a: &mut [E::G1Affine], - b_g1: &mut [E::G1Affine], - b_g2: &mut [E::G2Affine], - ext: &mut [E::G1Affine], - - // Inverse coefficient for ext elements - inv: &E::Fr, - - // Trapdoors - alpha: &E::Fr, - beta: &E::Fr, - - // Worker - worker: &Worker, - ) { - // Sanity check - assert_eq!(a.len(), at.len()); - assert_eq!(a.len(), bt.len()); - assert_eq!(a.len(), ct.len()); - assert_eq!(a.len(), b_g1.len()); - assert_eq!(a.len(), b_g2.len()); - assert_eq!(a.len(), ext.len()); - - // Evaluate polynomials in multiple threads - worker.scope(a.len(), |scope, chunk| { - for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a - .chunks_mut(chunk) - .zip(b_g1.chunks_mut(chunk)) - .zip(b_g2.chunks_mut(chunk)) - .zip(ext.chunks_mut(chunk)) - .zip(at.chunks(chunk)) - .zip(bt.chunks(chunk)) - .zip(ct.chunks(chunk)) - { - let mut g1_wnaf = g1_wnaf.shared(); - let mut g2_wnaf = g2_wnaf.shared(); - - scope.spawn(move |_scope| { - let mut a_proj = vec![E::G1::identity(); a.len()]; - let mut b_g1_proj = vec![E::G1::identity(); b_g1.len()]; - let mut b_g2_proj = vec![E::G2::identity(); b_g2.len()]; - let mut ext_proj = vec![E::G1::identity(); ext.len()]; - - for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a_proj - .iter_mut() - .zip(b_g1_proj.iter_mut()) - .zip(b_g2_proj.iter_mut()) - .zip(ext_proj.iter_mut()) - .zip(at.iter()) - .zip(bt.iter()) - .zip(ct.iter()) - { - fn eval_at_tau( - powers_of_tau: &[Scalar], - p: &[(S, usize)], - ) -> S { - let mut acc = S::zero(); - - for &(ref coeff, index) in p { - let mut n = powers_of_tau[index].0; - n.mul_assign(coeff); - acc.add_assign(&n); - } - - acc - } - - // Evaluate QAP polynomials at tau - let mut at = eval_at_tau(powers_of_tau, at); - let mut bt = eval_at_tau(powers_of_tau, bt); - let ct = eval_at_tau(powers_of_tau, ct); - - // Compute A query (in G1) - if !at.is_zero() { - *a = g1_wnaf.scalar(&at); - } - - // Compute B query (in G1/G2) - if !bt.is_zero() { - (); - *b_g1 = g1_wnaf.scalar(&bt); - *b_g2 = g2_wnaf.scalar(&bt); - } - - at.mul_assign(&beta); - bt.mul_assign(&alpha); - - let mut e = at; - e.add_assign(&bt); - e.add_assign(&ct); - e.mul_assign(inv); - - *ext = g1_wnaf.scalar(&e); - } - - // Batch normalize - E::G1::batch_normalize(&a_proj, a); - E::G1::batch_normalize(&b_g1_proj, b_g1); - E::G2::batch_normalize(&b_g2_proj, b_g2); - E::G1::batch_normalize(&ext_proj, ext); - }); - } - }); - } - - // Evaluate for inputs. - eval::( - &g1_wnaf, - &g2_wnaf, - &powers_of_tau, - &assembly.at_inputs, - &assembly.bt_inputs, - &assembly.ct_inputs, - &mut a[0..assembly.num_inputs], - &mut b_g1[0..assembly.num_inputs], - &mut b_g2[0..assembly.num_inputs], - &mut ic, - &gamma_inverse, - &alpha, - &beta, - &worker, - ); - - // Evaluate for auxiliary variables. - eval::( - &g1_wnaf, - &g2_wnaf, - &powers_of_tau, - &assembly.at_aux, - &assembly.bt_aux, - &assembly.ct_aux, - &mut a[assembly.num_inputs..], - &mut b_g1[assembly.num_inputs..], - &mut b_g2[assembly.num_inputs..], - &mut l, - &delta_inverse, - &alpha, - &beta, - &worker, - ); - - // Don't allow any elements be unconstrained, so that - // the L query is always fully dense. - for e in l.iter() { - if e.is_identity().into() { - return Err(SynthesisError::UnconstrainedVariable); - } - } - - let g1 = g1.to_affine(); - let g2 = g2.to_affine(); - - let vk = VerifyingKey:: { - alpha_g1: (g1 * &alpha).to_affine(), - beta_g1: (g1 * &beta).to_affine(), - beta_g2: (g2 * &beta).to_affine(), - gamma_g2: (g2 * &gamma).to_affine(), - delta_g1: (g1 * &delta).to_affine(), - delta_g2: (g2 * &delta).to_affine(), - ic, - }; - - Ok(Parameters { - vk, - h: Arc::new(h), - l: Arc::new(l), - - // Filter points at infinity away from A/B queries - a: Arc::new( - a.into_iter() - .filter(|e| bool::from(!e.is_identity())) - .collect(), - ), - b_g1: Arc::new( - b_g1.into_iter() - .filter(|e| bool::from(!e.is_identity())) - .collect(), - ), - b_g2: Arc::new( - b_g2.into_iter() - .filter(|e| bool::from(!e.is_identity())) - .collect(), - ), - }) -} diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs deleted file mode 100644 index 462dee38b5..0000000000 --- a/bellman/src/groth16/mod.rs +++ /dev/null @@ -1,567 +0,0 @@ -//! The [Groth16] proving system. -//! -//! [Groth16]: https://eprint.iacr.org/2016/260 - -use group::{prime::PrimeCurveAffine, GroupEncoding, UncompressedEncoding}; -use pairing::{Engine, MultiMillerLoop}; - -use crate::SynthesisError; - -use crate::multiexp::SourceBuilder; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use std::io::{self, Read, Write}; -use std::sync::Arc; - -#[cfg(test)] -mod tests; - -mod generator; -mod prover; -mod verifier; - -pub use self::generator::*; -pub use self::prover::*; -pub use self::verifier::*; - -#[derive(Clone)] -pub struct Proof { - pub a: E::G1Affine, - pub b: E::G2Affine, - pub c: E::G1Affine, -} - -impl PartialEq for Proof { - fn eq(&self, other: &Self) -> bool { - self.a == other.a && self.b == other.b && self.c == other.c - } -} - -impl Proof { - pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.a.to_bytes().as_ref())?; - writer.write_all(self.b.to_bytes().as_ref())?; - writer.write_all(self.c.to_bytes().as_ref())?; - - Ok(()) - } - - pub fn read(mut reader: R) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Repr::default(); - reader.read_exact(g1_repr.as_mut())?; - - let affine = E::G1Affine::from_bytes(&g1_repr); - let affine = if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) - }; - - affine.and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - }) - }; - - let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Repr::default(); - reader.read_exact(g2_repr.as_mut())?; - - let affine = E::G2Affine::from_bytes(&g2_repr); - let affine = if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) - }; - - affine.and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - }) - }; - - let a = read_g1(&mut reader)?; - let b = read_g2(&mut reader)?; - let c = read_g1(&mut reader)?; - - Ok(Proof { a, b, c }) - } -} - -#[derive(Clone)] -pub struct VerifyingKey { - // alpha in g1 for verifying and for creating A/C elements of - // proof. Never the point at infinity. - pub alpha_g1: E::G1Affine, - - // beta in g1 and g2 for verifying and for creating B/C elements - // of proof. Never the point at infinity. - pub beta_g1: E::G1Affine, - pub beta_g2: E::G2Affine, - - // gamma in g2 for verifying. Never the point at infinity. - pub gamma_g2: E::G2Affine, - - // delta in g1/g2 for verifying and proving, essentially the magic - // trapdoor that forces the prover to evaluate the C element of the - // proof with only components from the CRS. Never the point at - // infinity. - pub delta_g1: E::G1Affine, - pub delta_g2: E::G2Affine, - - // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma - // for all public inputs. Because all public inputs have a dummy constraint, - // this is the same size as the number of inputs, and never contains points - // at infinity. - pub ic: Vec, -} - -impl PartialEq for VerifyingKey { - fn eq(&self, other: &Self) -> bool { - self.alpha_g1 == other.alpha_g1 - && self.beta_g1 == other.beta_g1 - && self.beta_g2 == other.beta_g2 - && self.gamma_g2 == other.gamma_g2 - && self.delta_g1 == other.delta_g1 - && self.delta_g2 == other.delta_g2 - && self.ic == other.ic - } -} - -impl VerifyingKey { - pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.alpha_g1.to_uncompressed().as_ref())?; - writer.write_all(self.beta_g1.to_uncompressed().as_ref())?; - writer.write_all(self.beta_g2.to_uncompressed().as_ref())?; - writer.write_all(self.gamma_g2.to_uncompressed().as_ref())?; - writer.write_all(self.delta_g1.to_uncompressed().as_ref())?; - writer.write_all(self.delta_g2.to_uncompressed().as_ref())?; - writer.write_u32::(self.ic.len() as u32)?; - for ic in &self.ic { - writer.write_all(ic.to_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Uncompressed::default(); - reader.read_exact(g1_repr.as_mut())?; - - let affine = E::G1Affine::from_uncompressed(&g1_repr); - if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) - } - }; - - let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Uncompressed::default(); - reader.read_exact(g2_repr.as_mut())?; - - let affine = E::G2Affine::from_uncompressed(&g2_repr); - if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) - } - }; - - let alpha_g1 = read_g1(&mut reader)?; - let beta_g1 = read_g1(&mut reader)?; - let beta_g2 = read_g2(&mut reader)?; - let gamma_g2 = read_g2(&mut reader)?; - let delta_g1 = read_g1(&mut reader)?; - let delta_g2 = read_g2(&mut reader)?; - - let ic_len = reader.read_u32::()? as usize; - - let mut ic = vec![]; - - for _ in 0..ic_len { - let g1 = read_g1(&mut reader).and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - })?; - - ic.push(g1); - } - - Ok(VerifyingKey { - alpha_g1, - beta_g1, - beta_g2, - gamma_g2, - delta_g1, - delta_g2, - ic, - }) - } -} - -#[derive(Clone)] -pub struct Parameters { - pub vk: VerifyingKey, - - // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and - // m-2 inclusive. Never contains points at infinity. - pub h: Arc>, - - // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta - // for all auxiliary inputs. Variables can never be unconstrained, so this - // never contains points at infinity. - pub l: Arc>, - - // QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains - // points at infinity: polynomials that evaluate to zero are omitted from - // the CRS and the prover can deterministically skip their evaluation. - pub a: Arc>, - - // QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in - // G1 and G2 for C/B queries, respectively. Never contains points at - // infinity for the same reason as the "A" polynomials. - pub b_g1: Arc>, - pub b_g2: Arc>, -} - -impl PartialEq for Parameters { - fn eq(&self, other: &Self) -> bool { - self.vk == other.vk - && self.h == other.h - && self.l == other.l - && self.a == other.a - && self.b_g1 == other.b_g1 - && self.b_g2 == other.b_g2 - } -} - -impl Parameters { - pub fn write(&self, mut writer: W) -> io::Result<()> { - self.vk.write(&mut writer)?; - - writer.write_u32::(self.h.len() as u32)?; - for g in &self.h[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.l.len() as u32)?; - for g in &self.l[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.a.len() as u32)?; - for g in &self.a[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g1.len() as u32)?; - for g in &self.b_g1[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g2.len() as u32)?; - for g in &self.b_g2[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R, checked: bool) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); - reader.read_exact(repr.as_mut())?; - - let affine = if checked { - E::G1Affine::from_uncompressed(&repr) - } else { - E::G1Affine::from_uncompressed_unchecked(&repr) - }; - - let affine = if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G1")) - }; - - affine.and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - }) - }; - - let read_g2 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); - reader.read_exact(repr.as_mut())?; - - let affine = if checked { - E::G2Affine::from_uncompressed(&repr) - } else { - E::G2Affine::from_uncompressed_unchecked(&repr) - }; - - let affine = if affine.is_some().into() { - Ok(affine.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid G2")) - }; - - affine.and_then(|e| { - if e.is_identity().into() { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "point at infinity", - )) - } else { - Ok(e) - } - }) - }; - - let vk = VerifyingKey::::read(&mut reader)?; - - let mut h = vec![]; - let mut l = vec![]; - let mut a = vec![]; - let mut b_g1 = vec![]; - let mut b_g2 = vec![]; - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - h.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - l.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - a.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - b_g1.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - b_g2.push(read_g2(&mut reader)?); - } - } - - Ok(Parameters { - vk, - h: Arc::new(h), - l: Arc::new(l), - a: Arc::new(a), - b_g1: Arc::new(b_g1), - b_g2: Arc::new(b_g2), - }) - } -} - -pub struct PreparedVerifyingKey { - /// Pairing result of alpha*beta - alpha_g1_beta_g2: E::Gt, - /// -gamma in G2 - neg_gamma_g2: E::G2Prepared, - /// -delta in G2 - neg_delta_g2: E::G2Prepared, - /// Copy of IC from `VerifiyingKey`. - ic: Vec, -} - -pub trait ParameterSource { - type G1Builder: SourceBuilder; - type G2Builder: SourceBuilder; - - fn get_vk(&mut self, num_ic: usize) -> Result, SynthesisError>; - fn get_h(&mut self, num_h: usize) -> Result; - fn get_l(&mut self, num_l: usize) -> Result; - fn get_a( - &mut self, - num_inputs: usize, - num_aux: usize, - ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; - fn get_b_g1( - &mut self, - num_inputs: usize, - num_aux: usize, - ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; - fn get_b_g2( - &mut self, - num_inputs: usize, - num_aux: usize, - ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; -} - -impl<'a, E: Engine> ParameterSource for &'a Parameters { - type G1Builder = (Arc>, usize); - type G2Builder = (Arc>, usize); - - fn get_vk(&mut self, _: usize) -> Result, SynthesisError> { - Ok(self.vk.clone()) - } - - fn get_h(&mut self, _: usize) -> Result { - Ok((self.h.clone(), 0)) - } - - fn get_l(&mut self, _: usize) -> Result { - Ok((self.l.clone(), 0)) - } - - fn get_a( - &mut self, - num_inputs: usize, - _: usize, - ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> { - Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) - } - - fn get_b_g1( - &mut self, - num_inputs: usize, - _: usize, - ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> { - Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) - } - - fn get_b_g2( - &mut self, - num_inputs: usize, - _: usize, - ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> { - Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) - } -} - -#[cfg(test)] -mod test_with_bls12_381 { - use super::*; - use crate::{Circuit, ConstraintSystem, SynthesisError}; - - use bls12_381::{Bls12, Scalar}; - use ff::{Field, PrimeField}; - use rand::thread_rng; - use std::ops::MulAssign; - - #[test] - fn serialization() { - struct MySillyCircuit { - a: Option, - b: Option, - } - - impl Circuit for MySillyCircuit { - fn synthesize>( - self, - cs: &mut CS, - ) -> Result<(), SynthesisError> { - let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; - let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; - let c = cs.alloc_input( - || "c", - || { - let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; - let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; - - a.mul_assign(&b); - Ok(a) - }, - )?; - - cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c); - - Ok(()) - } - } - - let rng = &mut thread_rng(); - - let params = - generate_random_parameters::(MySillyCircuit { a: None, b: None }, rng) - .unwrap(); - - { - let mut v = vec![]; - - params.write(&mut v).unwrap(); - assert_eq!(v.len(), 2136); - - let de_params = Parameters::read(&v[..], true).unwrap(); - assert!(params == de_params); - - let de_params = Parameters::read(&v[..], false).unwrap(); - assert!(params == de_params); - } - - let pvk = prepare_verifying_key::(¶ms.vk); - - for _ in 0..100 { - let a = Scalar::random(rng); - let b = Scalar::random(rng); - let mut c = a; - c.mul_assign(&b); - - let proof = create_random_proof( - MySillyCircuit { - a: Some(a), - b: Some(b), - }, - ¶ms, - rng, - ) - .unwrap(); - - let mut v = vec![]; - proof.write(&mut v).unwrap(); - - assert_eq!(v.len(), 192); - - let de_proof = Proof::read(&v[..]).unwrap(); - assert!(proof == de_proof); - - assert!(verify_proof(&pvk, &proof, &[c]).is_ok()); - assert!(verify_proof(&pvk, &proof, &[a]).is_err()); - } - } -} diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs deleted file mode 100644 index 1f2d964a1b..0000000000 --- a/bellman/src/groth16/prover.rs +++ /dev/null @@ -1,339 +0,0 @@ -use rand_core::RngCore; -use std::ops::{AddAssign, MulAssign}; -use std::sync::Arc; - -use futures::Future; - -use ff::{Field, PrimeField}; -use group::{prime::PrimeCurveAffine, Curve}; -use pairing::Engine; - -use super::{ParameterSource, Proof}; - -use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use crate::domain::{EvaluationDomain, Scalar}; - -use crate::multiexp::{multiexp, DensityTracker, FullDensity}; - -use crate::multicore::Worker; - -fn eval( - lc: &LinearCombination, - mut input_density: Option<&mut DensityTracker>, - mut aux_density: Option<&mut DensityTracker>, - input_assignment: &[S], - aux_assignment: &[S], -) -> S { - let mut acc = S::zero(); - - for &(index, coeff) in lc.0.iter() { - let mut tmp; - - match index { - Variable(Index::Input(i)) => { - tmp = input_assignment[i]; - if let Some(ref mut v) = input_density { - v.inc(i); - } - } - Variable(Index::Aux(i)) => { - tmp = aux_assignment[i]; - if let Some(ref mut v) = aux_density { - v.inc(i); - } - } - } - - if coeff == S::one() { - acc.add_assign(&tmp); - } else { - tmp.mul_assign(&coeff); - acc.add_assign(&tmp); - } - } - - acc -} - -struct ProvingAssignment { - // Density of queries - a_aux_density: DensityTracker, - b_input_density: DensityTracker, - b_aux_density: DensityTracker, - - // Evaluations of A, B, C polynomials - a: Vec>, - b: Vec>, - c: Vec>, - - // Assignments of variables - input_assignment: Vec, - aux_assignment: Vec, -} - -impl ConstraintSystem for ProvingAssignment { - type Root = Self; - - fn alloc(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.aux_assignment.push(f()?); - self.a_aux_density.add_element(); - self.b_aux_density.add_element(); - - Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) - } - - fn alloc_input(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.input_assignment.push(f()?); - self.b_input_density.add_element(); - - Ok(Variable(Index::Input(self.input_assignment.len() - 1))) - } - - fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - let a = a(LinearCombination::zero()); - let b = b(LinearCombination::zero()); - let c = c(LinearCombination::zero()); - - self.a.push(Scalar(eval( - &a, - // Inputs have full density in the A query - // because there are constraints of the - // form x * 0 = 0 for each input. - None, - Some(&mut self.a_aux_density), - &self.input_assignment, - &self.aux_assignment, - ))); - self.b.push(Scalar(eval( - &b, - Some(&mut self.b_input_density), - Some(&mut self.b_aux_density), - &self.input_assignment, - &self.aux_assignment, - ))); - self.c.push(Scalar(eval( - &c, - // There is no C polynomial query, - // though there is an (beta)A + (alpha)B + C - // query for all aux variables. - // However, that query has full density. - None, - None, - &self.input_assignment, - &self.aux_assignment, - ))); - } - - fn push_namespace(&mut self, _: N) - where - NR: Into, - N: FnOnce() -> NR, - { - // Do nothing; we don't care about namespaces in this context. - } - - fn pop_namespace(&mut self) { - // Do nothing; we don't care about namespaces in this context. - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } -} - -pub fn create_random_proof>( - circuit: C, - params: P, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, - R: RngCore, -{ - let r = E::Fr::random(rng); - let s = E::Fr::random(rng); - - create_proof::(circuit, params, r, s) -} - -pub fn create_proof>( - circuit: C, - mut params: P, - r: E::Fr, - s: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, -{ - let mut prover = ProvingAssignment { - a_aux_density: DensityTracker::new(), - b_input_density: DensityTracker::new(), - b_aux_density: DensityTracker::new(), - a: vec![], - b: vec![], - c: vec![], - input_assignment: vec![], - aux_assignment: vec![], - }; - - prover.alloc_input(|| "", || Ok(E::Fr::one()))?; - - circuit.synthesize(&mut prover)?; - - for i in 0..prover.input_assignment.len() { - prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); - } - - let worker = Worker::new(); - - let vk = params.get_vk(prover.input_assignment.len())?; - - let h = { - let mut a = EvaluationDomain::from_coeffs(prover.a)?; - let mut b = EvaluationDomain::from_coeffs(prover.b)?; - let mut c = EvaluationDomain::from_coeffs(prover.c)?; - a.ifft(&worker); - a.coset_fft(&worker); - b.ifft(&worker); - b.coset_fft(&worker); - c.ifft(&worker); - c.coset_fft(&worker); - - a.mul_assign(&worker, &b); - drop(b); - a.sub_assign(&worker, &c); - drop(c); - a.divide_by_z_on_coset(&worker); - a.icoset_fft(&worker); - let mut a = a.into_coeffs(); - let a_len = a.len() - 1; - a.truncate(a_len); - // TODO: parallelize if it's even helpful - let a = Arc::new(a.into_iter().map(|s| s.0).collect::>()); - - multiexp(&worker, params.get_h(a.len())?, FullDensity, a) - }; - - // TODO: parallelize if it's even helpful - let input_assignment = Arc::new(prover.input_assignment); - let aux_assignment = Arc::new(prover.aux_assignment); - - let l = multiexp( - &worker, - params.get_l(aux_assignment.len())?, - FullDensity, - aux_assignment.clone(), - ); - - let a_aux_density_total = prover.a_aux_density.get_total_density(); - - let (a_inputs_source, a_aux_source) = - params.get_a(input_assignment.len(), a_aux_density_total)?; - - let a_inputs = multiexp( - &worker, - a_inputs_source, - FullDensity, - input_assignment.clone(), - ); - let a_aux = multiexp( - &worker, - a_aux_source, - Arc::new(prover.a_aux_density), - aux_assignment.clone(), - ); - - let b_input_density = Arc::new(prover.b_input_density); - let b_input_density_total = b_input_density.get_total_density(); - let b_aux_density = Arc::new(prover.b_aux_density); - let b_aux_density_total = b_aux_density.get_total_density(); - - let (b_g1_inputs_source, b_g1_aux_source) = - params.get_b_g1(b_input_density_total, b_aux_density_total)?; - - let b_g1_inputs = multiexp( - &worker, - b_g1_inputs_source, - b_input_density.clone(), - input_assignment.clone(), - ); - let b_g1_aux = multiexp( - &worker, - b_g1_aux_source, - b_aux_density.clone(), - aux_assignment.clone(), - ); - - let (b_g2_inputs_source, b_g2_aux_source) = - params.get_b_g2(b_input_density_total, b_aux_density_total)?; - - let b_g2_inputs = multiexp( - &worker, - b_g2_inputs_source, - b_input_density, - input_assignment, - ); - let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); - - if bool::from(vk.delta_g1.is_identity() | vk.delta_g2.is_identity()) { - // If this element is zero, someone is trying to perform a - // subversion-CRS attack. - return Err(SynthesisError::UnexpectedIdentity); - } - - let mut g_a = vk.delta_g1 * &r; - AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1); - let mut g_b = vk.delta_g2 * &s; - AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2); - let mut g_c; - { - let mut rs = r; - rs.mul_assign(&s); - - g_c = vk.delta_g1 * &rs; - AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.alpha_g1 * &s)); - AddAssign::<&E::G1>::add_assign(&mut g_c, &(vk.beta_g1 * &r)); - } - let mut a_answer = a_inputs.wait()?; - AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); - AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer); - MulAssign::::mul_assign(&mut a_answer, s); - AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer); - - let mut b1_answer: E::G1 = b_g1_inputs.wait()?; - AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?); - let mut b2_answer = b_g2_inputs.wait()?; - AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?); - - AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer); - MulAssign::::mul_assign(&mut b1_answer, r); - AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer); - AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?); - AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); - - Ok(Proof { - a: g_a.to_affine(), - b: g_b.to_affine(), - c: g_c.to_affine(), - }) -} diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs deleted file mode 100644 index fd7d2b9150..0000000000 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ /dev/null @@ -1,491 +0,0 @@ -use ff::{Field, PrimeField}; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, -}; -use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; - -use rand_core::RngCore; -use std::fmt; -use std::iter::Sum; -use std::num::Wrapping; -use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -const MODULUS_R: Wrapping = Wrapping(64513); - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Fr(Wrapping); - -impl Default for Fr { - fn default() -> Self { - ::zero() - } -} - -impl ConstantTimeEq for Fr { - fn ct_eq(&self, other: &Fr) -> Choice { - (self.0).0.ct_eq(&(other.0).0) - } -} - -impl fmt::Display for Fr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", (self.0).0) - } -} - -impl From for Fr { - fn from(v: u64) -> Fr { - Fr(Wrapping((v % MODULUS_R.0 as u64) as u32)) - } -} - -impl ConditionallySelectable for Fr { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fr(Wrapping(u32::conditional_select( - &(a.0).0, - &(b.0).0, - choice, - ))) - } -} - -impl Sum for Fr { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), ::std::ops::Add::add) - } -} - -impl<'r> Sum<&'r Fr> for Fr { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), ::std::ops::Add::add) - } -} - -impl Neg for Fr { - type Output = Self; - - fn neg(mut self) -> Self { - if !::is_zero(&self) { - self.0 = MODULUS_R - self.0; - } - self - } -} - -impl<'r> Add<&'r Fr> for Fr { - type Output = Self; - - fn add(self, other: &Self) -> Self { - let mut ret = self; - AddAssign::add_assign(&mut ret, other); - ret - } -} - -impl Add for Fr { - type Output = Self; - - fn add(self, other: Self) -> Self { - self + &other - } -} - -impl<'r> AddAssign<&'r Fr> for Fr { - fn add_assign(&mut self, other: &Self) { - self.0 = (self.0 + other.0) % MODULUS_R; - } -} - -impl AddAssign for Fr { - fn add_assign(&mut self, other: Self) { - AddAssign::add_assign(self, &other); - } -} - -impl<'r> Sub<&'r Fr> for Fr { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - let mut ret = self; - SubAssign::sub_assign(&mut ret, other); - ret - } -} - -impl Sub for Fr { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self - &other - } -} - -impl<'r> SubAssign<&'r Fr> for Fr { - fn sub_assign(&mut self, other: &Self) { - self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; - } -} - -impl SubAssign for Fr { - fn sub_assign(&mut self, other: Self) { - SubAssign::sub_assign(self, &other); - } -} - -impl<'r> Mul<&'r Fr> for Fr { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - MulAssign::mul_assign(&mut ret, other); - ret - } -} - -impl Mul for Fr { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self * &other - } -} - -impl<'r> MulAssign<&'r Fr> for Fr { - fn mul_assign(&mut self, other: &Self) { - self.0 = (self.0 * other.0) % MODULUS_R; - } -} - -impl MulAssign for Fr { - fn mul_assign(&mut self, other: Self) { - MulAssign::mul_assign(self, &other); - } -} - -impl BitAnd for Fr { - type Output = u64; - - fn bitand(self, rhs: u64) -> u64 { - (self.0).0 as u64 & rhs - } -} - -impl Shr for Fr { - type Output = Fr; - - fn shr(mut self, rhs: u32) -> Fr { - self.0 = Wrapping((self.0).0 >> rhs); - self - } -} - -impl Field for Fr { - fn random(rng: &mut R) -> Self { - Fr(Wrapping(rng.next_u32()) % MODULUS_R) - } - - fn zero() -> Self { - Fr(Wrapping(0)) - } - - fn one() -> Self { - Fr(Wrapping(1)) - } - - fn is_zero(&self) -> bool { - (self.0).0 == 0 - } - - fn square(&self) -> Self { - Fr((self.0 * self.0) % MODULUS_R) - } - - fn double(&self) -> Self { - Fr((self.0 << 1) % MODULUS_R) - } - - fn invert(&self) -> CtOption { - if ::is_zero(self) { - CtOption::new(::zero(), Choice::from(0)) - } else { - CtOption::new( - self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]), - Choice::from(1), - ) - } - } - - fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - let mut c = Fr::root_of_unity(); - // r = self^((t + 1) // 2) - let mut r = self.pow_vartime([32u64]); - // t = self^t - let mut t = self.pow_vartime([63u64]); - let mut m = Fr::S; - - while t != ::one() { - let mut i = 1; - { - let mut t2i = t.square(); - loop { - if t2i == ::one() { - break; - } - t2i = t2i.square(); - i += 1; - } - } - - for _ in 0..(m - i - 1) { - c = c.square(); - } - MulAssign::mul_assign(&mut r, &c); - c = c.square(); - MulAssign::mul_assign(&mut t, &c); - m = i; - } - - CtOption::new(r, (r * r).ct_eq(self)) - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FrRepr([u8; 8]); - -impl From for FrRepr { - fn from(v: Fr) -> FrRepr { - FrRepr::from(&v) - } -} - -impl<'a> From<&'a Fr> for FrRepr { - fn from(v: &'a Fr) -> FrRepr { - FrRepr(((v.0).0 as u64).to_le_bytes()) - } -} - -impl AsMut<[u8]> for FrRepr { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl AsRef<[u8]> for FrRepr { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl Default for FrRepr { - fn default() -> FrRepr { - FrRepr([0; 8]) - } -} - -impl PrimeField for Fr { - type Repr = FrRepr; - type ReprEndianness = byteorder::LittleEndian; - - const NUM_BITS: u32 = 16; - const CAPACITY: u32 = 15; - const S: u32 = 10; - - fn from_repr(repr: FrRepr) -> Option { - let v = u64::from_le_bytes(repr.0); - if v >= (MODULUS_R.0 as u64) { - None - } else { - Some(Fr(Wrapping(v as u32))) - } - } - - fn to_repr(&self) -> FrRepr { - FrRepr::from(*self) - } - - fn is_odd(&self) -> bool { - (self.0).0 % 2 != 0 - } - - fn char() -> FrRepr { - Fr(MODULUS_R).into() - } - - fn multiplicative_generator() -> Fr { - Fr(Wrapping(5)) - } - - fn root_of_unity() -> Fr { - Fr(Wrapping(57751)) - } -} - -#[derive(Clone)] -pub struct DummyEngine; - -impl Engine for DummyEngine { - type Fr = Fr; - type G1 = Fr; - type G1Affine = Fr; - type G2 = Fr; - type G2Affine = Fr; - - // TODO: This should be F_645131 or something. Doesn't matter for now. - type Gt = Fr; - - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::multi_miller_loop(&[(p, &(*q).into())]).final_exponentiation() - } -} - -impl MultiMillerLoop for DummyEngine { - type G2Prepared = Fr; - // TODO: This should be F_645131 or something. Doesn't matter for now. - type Result = Fr; - - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { - let mut acc = ::zero(); - - for &(a, b) in terms { - let mut tmp = *a; - MulAssign::mul_assign(&mut tmp, b); - AddAssign::add_assign(&mut acc, &tmp); - } - - acc - } -} - -impl MillerLoopResult for Fr { - type Gt = Fr; - - /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(&self) -> Self::Gt { - *self - } -} - -impl Group for Fr { - type Scalar = Fr; - - fn random(rng: &mut R) -> Self { - ::random(rng) - } - - fn identity() -> Self { - ::zero() - } - - fn generator() -> Self { - ::one() - } - - fn is_identity(&self) -> Choice { - Choice::from(if ::is_zero(self) { 1 } else { 0 }) - } - - fn double(&self) -> Self { - ::double(self) - } -} - -impl PrimeGroup for Fr {} - -impl Curve for Fr { - type AffineRepr = Fr; - - fn to_affine(&self) -> Fr { - *self - } -} - -impl WnafGroup for Fr { - fn recommended_wnaf_for_num_scalars(_: usize) -> usize { - 3 - } -} - -impl PrimeCurve for Fr { - type Affine = Fr; -} - -#[derive(Copy, Clone, Default)] -pub struct FakePoint; - -impl AsMut<[u8]> for FakePoint { - fn as_mut(&mut self) -> &mut [u8] { - unimplemented!() - } -} - -impl AsRef<[u8]> for FakePoint { - fn as_ref(&self) -> &[u8] { - unimplemented!() - } -} - -impl PrimeCurveAffine for Fr { - type Curve = Fr; - type Scalar = Fr; - - fn identity() -> Self { - ::zero() - } - - fn generator() -> Self { - ::one() - } - - fn is_identity(&self) -> Choice { - Choice::from(if ::is_zero(self) { 1 } else { 0 }) - } - - fn to_curve(&self) -> Self::Curve { - *self - } -} - -impl GroupEncoding for Fr { - type Repr = FakePoint; - - fn from_bytes(_bytes: &Self::Repr) -> CtOption { - unimplemented!() - } - - fn from_bytes_unchecked(_bytes: &Self::Repr) -> CtOption { - unimplemented!() - } - - fn to_bytes(&self) -> Self::Repr { - unimplemented!() - } -} - -impl UncompressedEncoding for Fr { - type Uncompressed = FakePoint; - - fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { - unimplemented!() - } - - fn from_uncompressed_unchecked(_bytes: &Self::Uncompressed) -> CtOption { - unimplemented!() - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - unimplemented!() - } -} - -impl PairingCurveAffine for Fr { - type Pair = Fr; - type PairingResult = Fr; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - self.mul(*other) - } -} diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs deleted file mode 100644 index 371f7c16fa..0000000000 --- a/bellman/src/groth16/tests/mod.rs +++ /dev/null @@ -1,381 +0,0 @@ -use ff::{Field, PrimeField}; - -mod dummy_engine; -use self::dummy_engine::*; - -use std::marker::PhantomData; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use crate::{Circuit, ConstraintSystem, SynthesisError}; - -use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; - -struct XORDemo { - a: Option, - b: Option, - _marker: PhantomData, -} - -impl Circuit for XORDemo { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - let a_var = cs.alloc( - || "a", - || { - if self.a.is_some() { - if self.a.unwrap() { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - }, - )?; - - cs.enforce( - || "a_boolean_constraint", - |lc| lc + CS::one() - a_var, - |lc| lc + a_var, - |lc| lc, - ); - - let b_var = cs.alloc( - || "b", - || { - if self.b.is_some() { - if self.b.unwrap() { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - }, - )?; - - cs.enforce( - || "b_boolean_constraint", - |lc| lc + CS::one() - b_var, - |lc| lc + b_var, - |lc| lc, - ); - - let c_var = cs.alloc_input( - || "c", - || { - if self.a.is_some() && self.b.is_some() { - if self.a.unwrap() ^ self.b.unwrap() { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - }, - )?; - - cs.enforce( - || "c_xor_constraint", - |lc| lc + a_var + a_var, - |lc| lc + b_var, - |lc| lc + a_var + b_var - c_var, - ); - - Ok(()) - } -} - -#[test] -fn test_xordemo() { - let g1 = Fr::one(); - let g2 = Fr::one(); - let alpha = Fr::from_str("48577").unwrap(); - let beta = Fr::from_str("22580").unwrap(); - let gamma = Fr::from_str("53332").unwrap(); - let delta = Fr::from_str("5481").unwrap(); - let tau = Fr::from_str("3673").unwrap(); - - let params = { - let c = XORDemo { - a: None, - b: None, - _marker: PhantomData, - }; - - generate_parameters::(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap() - }; - - // This will synthesize the constraint system: - // - // public inputs: a_0 = 1, a_1 = c - // aux inputs: a_2 = a, a_3 = b - // constraints: - // (a_0 - a_2) * (a_2) = 0 - // (a_0 - a_3) * (a_3) = 0 - // (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1) - // (a_0) * 0 = 0 - // (a_1) * 0 = 0 - - // The evaluation domain is 8. The H query should - // have 7 elements (it's a quotient polynomial) - assert_eq!(7, params.h.len()); - - let mut root_of_unity = Fr::root_of_unity(); - - // We expect this to be a 2^10 root of unity - assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 10])); - - // Let's turn it into a 2^3 root of unity. - root_of_unity = root_of_unity.pow_vartime(&[1u64 << 7]); - assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 3])); - assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity); - - // Let's compute all the points in our evaluation domain. - let mut points = Vec::with_capacity(8); - for i in 0u64..8 { - points.push(root_of_unity.pow_vartime(&[i])); - } - - // Let's compute t(tau) = (tau - p_0)(tau - p_1)... - // = tau^8 - 1 - let mut t_at_tau = tau.pow_vartime(&[8u64]); - t_at_tau.sub_assign(&Fr::one()); - { - let mut tmp = Fr::one(); - for p in &points { - let mut term = tau; - term.sub_assign(p); - tmp.mul_assign(&term); - } - assert_eq!(tmp, t_at_tau); - } - - // We expect our H query to be 7 elements of the form... - // {tau^i t(tau) / delta} - let delta_inverse = delta.invert().unwrap(); - let gamma_inverse = gamma.invert().unwrap(); - { - let mut coeff = delta_inverse; - coeff.mul_assign(&t_at_tau); - - let mut cur = Fr::one(); - for h in params.h.iter() { - let mut tmp = cur; - tmp.mul_assign(&coeff); - - assert_eq!(*h, tmp); - - cur.mul_assign(&tau); - } - } - - // The density of the IC query is 2 (2 inputs) - assert_eq!(2, params.vk.ic.len()); - - // The density of the L query is 2 (2 aux variables) - assert_eq!(2, params.l.len()); - - // The density of the A query is 4 (each variable is in at least one A term) - assert_eq!(4, params.a.len()); - - // The density of the B query is 2 (two variables are in at least one B term) - assert_eq!(2, params.b_g1.len()); - assert_eq!(2, params.b_g2.len()); - - /* - Lagrange interpolation polynomials in our evaluation domain: - - ,-------------------------------. ,-------------------------------. ,-------------------------------. - | A TERM | | B TERM | | C TERM | - `-------------------------------. `-------------------------------' `-------------------------------' - | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | - | 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 | - | 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 | - | 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 | - | 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | - | 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | - `-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------' - - Example for u_0: - - sage: r = 64513 - sage: Fr = GF(r) - sage: omega = (Fr(5)^63)^(2^7) - sage: tau = Fr(3673) - sage: R. = PolynomialRing(Fr, 'x') - sage: def eval(tau, c0, c1, c2, c3, c4): - ....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)]) - ....: return p.substitute(tau) - sage: eval(tau, 1, 1, 0, 1, 0) - 59158 - */ - - let u_i = [59158, 48317, 21767, 10402] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - let v_i = [0, 0, 60619, 30791] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - let w_i = [0, 23320, 41193, 41193] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - - for (u, a) in u_i.iter().zip(¶ms.a[..]) { - assert_eq!(u, a); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.b_g1[..]) - { - assert_eq!(v, b); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.b_g2[..]) - { - assert_eq!(v, b); - } - - for i in 0..4 { - let mut tmp1 = beta; - tmp1.mul_assign(&u_i[i]); - - let mut tmp2 = alpha; - tmp2.mul_assign(&v_i[i]); - - tmp1.add_assign(&tmp2); - tmp1.add_assign(&w_i[i]); - - if i < 2 { - // Check the correctness of the IC query elements - tmp1.mul_assign(&gamma_inverse); - - assert_eq!(tmp1, params.vk.ic[i]); - } else { - // Check the correctness of the L query elements - tmp1.mul_assign(&delta_inverse); - - assert_eq!(tmp1, params.l[i - 2]); - } - } - - // Check consistency of the other elements - assert_eq!(alpha, params.vk.alpha_g1); - assert_eq!(beta, params.vk.beta_g1); - assert_eq!(beta, params.vk.beta_g2); - assert_eq!(gamma, params.vk.gamma_g2); - assert_eq!(delta, params.vk.delta_g1); - assert_eq!(delta, params.vk.delta_g2); - - let pvk = prepare_verifying_key(¶ms.vk); - - let r = Fr::from_str("27134").unwrap(); - let s = Fr::from_str("17146").unwrap(); - - let proof = { - let c = XORDemo { - a: Some(true), - b: Some(false), - _marker: PhantomData, - }; - - create_proof(c, ¶ms, r, s).unwrap() - }; - - // A(x) = - // a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) + - // a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) + - // a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) + - // a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) + - { - // proof A = alpha + A(tau) + delta * r - let mut expected_a = delta; - expected_a.mul_assign(&r); - expected_a.add_assign(&alpha); - expected_a.add_assign(&u_i[0]); // a_0 = 1 - expected_a.add_assign(&u_i[1]); // a_1 = 1 - expected_a.add_assign(&u_i[2]); // a_2 = 1 - // a_3 = 0 - assert_eq!(proof.a, expected_a); - } - - // B(x) = - // a_0 * (0) + - // a_1 * (0) + - // a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) + - // a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385) - { - // proof B = beta + B(tau) + delta * s - let mut expected_b = delta; - expected_b.mul_assign(&s); - expected_b.add_assign(&beta); - expected_b.add_assign(&v_i[0]); // a_0 = 1 - expected_b.add_assign(&v_i[1]); // a_1 = 1 - expected_b.add_assign(&v_i[2]); // a_2 = 1 - // a_3 = 0 - assert_eq!(proof.b, expected_b); - } - - // C(x) = - // a_0 * (0) + - // a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) + - // a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + - // a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) - // - // If A * B = C at each point in the domain, then the following polynomial... - // P(x) = A(x) * B(x) - C(x) - // = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481 - // - // ... should be divisible by t(x), producing the quotient polynomial: - // h(x) = P(x) / t(x) - // = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032 - { - let mut expected_c = Fr::zero(); - - // A * s - let mut tmp = proof.a; - tmp.mul_assign(&s); - expected_c.add_assign(&tmp); - - // B * r - let mut tmp = proof.b; - tmp.mul_assign(&r); - expected_c.add_assign(&tmp); - - // delta * r * s - let mut tmp = delta; - tmp.mul_assign(&r); - tmp.mul_assign(&s); - expected_c.sub_assign(&tmp); - - // L query answer - // a_2 = 1, a_3 = 0 - expected_c.add_assign(¶ms.l[0]); - - // H query answer - for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739] - .iter() - .enumerate() - { - let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); - - let mut tmp = params.h[i]; - tmp.mul_assign(&coeff); - expected_c.add_assign(&tmp); - } - - assert_eq!(expected_c, proof.c); - } - - assert!(verify_proof(&pvk, &proof, &[Fr::one()]).is_ok()); -} diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs deleted file mode 100644 index 43c69cb669..0000000000 --- a/bellman/src/groth16/verifier.rs +++ /dev/null @@ -1,56 +0,0 @@ -use group::{prime::PrimeCurveAffine, Curve}; -use pairing::{MillerLoopResult, MultiMillerLoop}; -use std::ops::{AddAssign, Neg}; - -use super::{PreparedVerifyingKey, Proof, VerifyingKey}; - -use crate::VerificationError; - -pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { - let gamma = vk.gamma_g2.neg(); - let delta = vk.delta_g2.neg(); - - PreparedVerifyingKey { - alpha_g1_beta_g2: E::pairing(&vk.alpha_g1, &vk.beta_g2), - neg_gamma_g2: gamma.into(), - neg_delta_g2: delta.into(), - ic: vk.ic.clone(), - } -} - -pub fn verify_proof<'a, E: MultiMillerLoop>( - pvk: &'a PreparedVerifyingKey, - proof: &Proof, - public_inputs: &[E::Fr], -) -> Result<(), VerificationError> { - if (public_inputs.len() + 1) != pvk.ic.len() { - return Err(VerificationError::InvalidVerifyingKey); - } - - let mut acc = pvk.ic[0].to_curve(); - - for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { - AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i)); - } - - // The original verification equation is: - // A * B = alpha * beta + inputs * gamma + C * delta - // ... however, we rearrange it so that it is: - // A * B - inputs * gamma - C * delta = alpha * beta - // or equivalently: - // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta - // which allows us to do a single final exponentiation. - - if pvk.alpha_g1_beta_g2 - == E::multi_miller_loop(&[ - (&proof.a, &proof.b.into()), - (&acc.to_affine(), &pvk.neg_gamma_g2), - (&proof.c, &pvk.neg_delta_g2), - ]) - .final_exponentiation() - { - Ok(()) - } else { - Err(VerificationError::InvalidProof) - } -} diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs deleted file mode 100644 index 9eb44dd1d8..0000000000 --- a/bellman/src/lib.rs +++ /dev/null @@ -1,575 +0,0 @@ -//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit -//! traits and and primitive structures, as well as basic gadget implementations -//! such as booleans and number abstractions. -//! -//! # Example circuit -//! -//! Say we want to write a circuit that proves we know the preimage to some hash -//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a -//! fixed length known in advance (because the circuit parameters will depend on -//! it), but can otherwise have any value. We take the following strategy: -//! -//! - Witness each bit of the preimage. -//! - Compute `hash = SHA-256d(preimage)` inside the circuit. -//! - Expose `hash` as a public input using multiscalar packing. -//! -//! ``` -//! use bellman::{ -//! gadgets::{ -//! boolean::{AllocatedBit, Boolean}, -//! multipack, -//! sha256::sha256, -//! }, -//! groth16, Circuit, ConstraintSystem, SynthesisError, -//! }; -//! use bls12_381::Bls12; -//! use ff::PrimeField; -//! use pairing::Engine; -//! use rand::rngs::OsRng; -//! use sha2::{Digest, Sha256}; -//! -//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order. -//! fn sha256d>( -//! mut cs: CS, -//! data: &[Boolean], -//! ) -> Result, SynthesisError> { -//! // Flip endianness of each input byte -//! let input: Vec<_> = data -//! .chunks(8) -//! .map(|c| c.iter().rev()) -//! .flatten() -//! .cloned() -//! .collect(); -//! -//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?; -//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?; -//! -//! // Flip endianness of each output byte -//! Ok(res -//! .chunks(8) -//! .map(|c| c.iter().rev()) -//! .flatten() -//! .cloned() -//! .collect()) -//! } -//! -//! struct MyCircuit { -//! /// The input to SHA-256d we are proving that we know. Set to `None` when we -//! /// are verifying a proof (and do not have the witness data). -//! preimage: Option<[u8; 80]>, -//! } -//! -//! impl Circuit for MyCircuit { -//! fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { -//! // Compute the values for the bits of the preimage. If we are verifying a proof, -//! // we still need to create the same constraints, so we return an equivalent-size -//! // Vec of None (indicating that the value of each bit is unknown). -//! let bit_values = if let Some(preimage) = self.preimage { -//! preimage -//! .into_iter() -//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)) -//! .flatten() -//! .map(|b| Some(b)) -//! .collect() -//! } else { -//! vec![None; 80 * 8] -//! }; -//! assert_eq!(bit_values.len(), 80 * 8); -//! -//! // Witness the bits of the preimage. -//! let preimage_bits = bit_values -//! .into_iter() -//! .enumerate() -//! // Allocate each bit. -//! .map(|(i, b)| { -//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b) -//! }) -//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget). -//! .map(|b| b.map(Boolean::from)) -//! .collect::, _>>()?; -//! -//! // Compute hash = SHA-256d(preimage). -//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?; -//! -//! // Expose the vector of 32 boolean variables as compact public inputs. -//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash) -//! } -//! } -//! -//! // Create parameters for our circuit. In a production deployment these would -//! // be generated securely using a multiparty computation. -//! let params = { -//! let c = MyCircuit { preimage: None }; -//! groth16::generate_random_parameters::(c, &mut OsRng).unwrap() -//! }; -//! -//! // Prepare the verification key (for proof verification). -//! let pvk = groth16::prepare_verifying_key(¶ms.vk); -//! -//! // Pick a preimage and compute its hash. -//! let preimage = [42; 80]; -//! let hash = Sha256::digest(&Sha256::digest(&preimage)); -//! -//! // Create an instance of our circuit (with the preimage as a witness). -//! let c = MyCircuit { -//! preimage: Some(preimage), -//! }; -//! -//! // Create a Groth16 proof with our parameters. -//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap(); -//! -//! // Pack the hash as inputs for proof verification. -//! let hash_bits = multipack::bytes_to_bits_le(&hash); -//! let inputs = multipack::compute_multipacking(&hash_bits); -//! -//! // Check the proof! -//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok()); -//! ``` -//! -//! # Roadmap -//! -//! `bellman` is being refactored into a generic proving library. Currently it -//! is pairing-specific, and different types of proving systems need to be -//! implemented as sub-modules. After the refactor, `bellman` will be generic -//! using the [`ff`] and [`group`] crates, while specific proving systems will -//! be separate crates that pull in the dependencies they require. - -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] - -pub mod domain; -pub mod gadgets; -#[cfg(feature = "groth16")] -pub mod groth16; -pub mod multicore; -mod multiexp; - -use ff::PrimeField; - -use std::error::Error; -use std::fmt; -use std::io; -use std::marker::PhantomData; -use std::ops::{Add, Sub}; - -/// Computations are expressed in terms of arithmetic circuits, in particular -/// rank-1 quadratic constraint systems. The `Circuit` trait represents a -/// circuit that can be synthesized. The `synthesize` method is called during -/// CRS generation and during proving. -pub trait Circuit { - /// Synthesize the circuit into a rank-1 quadratic constraint system - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError>; -} - -/// Represents a variable in our constraint system. -#[derive(Copy, Clone, Debug)] -pub struct Variable(Index); - -impl Variable { - /// This constructs a variable with an arbitrary index. - /// Circuit implementations are not recommended to use this. - pub fn new_unchecked(idx: Index) -> Variable { - Variable(idx) - } - - /// This returns the index underlying the variable. - /// Circuit implementations are not recommended to use this. - pub fn get_unchecked(&self) -> Index { - self.0 - } -} - -/// Represents the index of either an input variable or -/// auxiliary variable. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Index { - Input(usize), - Aux(usize), -} - -/// This represents a linear combination of some variables, with coefficients -/// in the scalar field of a pairing-friendly elliptic curve group. -#[derive(Clone)] -pub struct LinearCombination(Vec<(Variable, Scalar)>); - -impl AsRef<[(Variable, Scalar)]> for LinearCombination { - fn as_ref(&self) -> &[(Variable, Scalar)] { - &self.0 - } -} - -impl LinearCombination { - pub fn zero() -> LinearCombination { - LinearCombination(vec![]) - } -} - -impl Add<(Scalar, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination { - self.0.push((var, coeff)); - - self - } -} - -impl Sub<(Scalar, Variable)> for LinearCombination { - type Output = LinearCombination; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination { - self + (coeff.neg(), var) - } -} - -impl Add for LinearCombination { - type Output = LinearCombination; - - fn add(self, other: Variable) -> LinearCombination { - self + (Scalar::one(), other) - } -} - -impl Sub for LinearCombination { - type Output = LinearCombination; - - fn sub(self, other: Variable) -> LinearCombination { - self - (Scalar::one(), other) - } -} - -impl<'a, Scalar: PrimeField> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self + (s.1, s.0); - } - - self - } -} - -impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self - (s.1, s.0); - } - - self - } -} - -impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination)> - for LinearCombination -{ - type Output = LinearCombination; - - fn add( - mut self, - (coeff, other): (Scalar, &'a LinearCombination), - ) -> LinearCombination { - for s in &other.0 { - let mut tmp = s.1; - tmp.mul_assign(&coeff); - self = self + (tmp, s.0); - } - - self - } -} - -impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination)> - for LinearCombination -{ - type Output = LinearCombination; - - fn sub( - mut self, - (coeff, other): (Scalar, &'a LinearCombination), - ) -> LinearCombination { - for s in &other.0 { - let mut tmp = s.1; - tmp.mul_assign(&coeff); - self = self - (tmp, s.0); - } - - self - } -} - -/// This is an error that could occur during circuit synthesis contexts, -/// such as CRS generation or proving. -#[derive(Debug)] -pub enum SynthesisError { - /// During synthesis, we lacked knowledge of a variable assignment. - AssignmentMissing, - /// During synthesis, we divided by zero. - DivisionByZero, - /// During synthesis, we constructed an unsatisfiable constraint system. - Unsatisfiable, - /// During synthesis, our polynomials ended up being too high of degree - PolynomialDegreeTooLarge, - /// During proof generation, we encountered an identity in the CRS - UnexpectedIdentity, - /// During proof generation, we encountered an I/O error with the CRS - IoError(io::Error), - /// During CRS generation, we observed an unconstrained auxiliary variable - UnconstrainedVariable, -} - -impl From for SynthesisError { - fn from(e: io::Error) -> SynthesisError { - SynthesisError::IoError(e) - } -} - -impl Error for SynthesisError { - fn description(&self) -> &str { - match *self { - SynthesisError::AssignmentMissing => { - "an assignment for a variable could not be computed" - } - SynthesisError::DivisionByZero => "division by zero", - SynthesisError::Unsatisfiable => "unsatisfiable constraint system", - SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", - SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", - SynthesisError::IoError(_) => "encountered an I/O error", - SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained", - } - } -} - -impl fmt::Display for SynthesisError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - if let SynthesisError::IoError(ref e) = *self { - write!(f, "I/O error: ")?; - e.fmt(f) - } else { - write!(f, "{}", self) - } - } -} - -/// An error during verification. -#[derive(Debug, Clone)] -pub enum VerificationError { - /// Verification was attempted with a malformed verifying key. - InvalidVerifyingKey, - /// Proof verification failed. - InvalidProof, -} - -impl Error for VerificationError { - fn description(&self) -> &str { - match *self { - VerificationError::InvalidVerifyingKey => "malformed verifying key", - VerificationError::InvalidProof => "proof verification failed", - } - } -} - -impl fmt::Display for VerificationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self) - } -} - -/// Represents a constraint system which can have new variables -/// allocated and constrains between them formed. -pub trait ConstraintSystem: Sized { - /// Represents the type of the "root" of this constraint system - /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; - - /// Return the "one" input variable - fn one() -> Variable { - Variable::new_unchecked(Index::Input(0)) - } - - /// Allocate a private variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. The given `annotation` function is invoked - /// in testing contexts in order to derive a unique name for this variable in the current - /// namespace. - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// Allocate a public variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts - /// in order to derive a unique name for the constraint in the current namespace. - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination; - - /// Create a new (sub)namespace and enter into it. Not intended - /// for downstream use; use `namespace` instead. - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR; - - /// Exit out of the existing namespace. Not intended for - /// downstream use; use `namespace` instead. - fn pop_namespace(&mut self); - - /// Gets the "root" constraint system, bypassing the namespacing. - /// Not intended for downstream use; use `namespace` instead. - fn get_root(&mut self) -> &mut Self::Root; - - /// Begin a namespace for this constraint system. - fn namespace(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root> - where - NR: Into, - N: FnOnce() -> NR, - { - self.get_root().push_namespace(name_fn); - - Namespace(self.get_root(), PhantomData) - } -} - -/// This is a "namespaced" constraint system which borrows a constraint system (pushing -/// a namespace context) and, when dropped, pops out of the namespace context. -pub struct Namespace<'a, Scalar: PrimeField, CS: ConstraintSystem>( - &'a mut CS, - PhantomData, -); - -impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem - for Namespace<'cs, Scalar, CS> -{ - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - self.0.enforce(annotation, a, b, c) - } - - // Downstream users who use `namespace` will never interact with these - // functions and they will never be invoked because the namespace is - // never a root constraint system. - - fn push_namespace(&mut self, _: N) - where - NR: Into, - N: FnOnce() -> NR, - { - panic!("only the root's push_namespace should be called"); - } - - fn pop_namespace(&mut self) { - panic!("only the root's pop_namespace should be called"); - } - - fn get_root(&mut self) -> &mut Self::Root { - self.0.get_root() - } -} - -impl<'a, Scalar: PrimeField, CS: ConstraintSystem> Drop for Namespace<'a, Scalar, CS> { - fn drop(&mut self) { - self.get_root().pop_namespace() - } -} - -/// Convenience implementation of ConstraintSystem for mutable references to -/// constraint systems. -impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem - for &'cs mut CS -{ - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - (**self).enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR, - { - (**self).push_namespace(name_fn) - } - - fn pop_namespace(&mut self) { - (**self).pop_namespace() - } - - fn get_root(&mut self) -> &mut Self::Root { - (**self).get_root() - } -} diff --git a/bellman/src/multicore.rs b/bellman/src/multicore.rs deleted file mode 100644 index ba69b5f338..0000000000 --- a/bellman/src/multicore.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! An interface for dealing with the kinds of parallel computations involved in -//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and -//! [`crossbeam`] but may be extended in the future to allow for various -//! parallelism strategies. -//! -//! [`CpuPool`]: futures_cpupool::CpuPool - -#[cfg(feature = "multicore")] -mod implementation { - use crossbeam::{self, thread::Scope}; - use futures::{Future, IntoFuture, Poll}; - use futures_cpupool::{CpuFuture, CpuPool}; - use num_cpus; - - #[derive(Clone)] - pub struct Worker { - cpus: usize, - pool: CpuPool, - } - - impl Worker { - // We don't expose this outside the library so that - // all `Worker` instances have the same number of - // CPUs configured. - pub(crate) fn new_with_cpus(cpus: usize) -> Worker { - Worker { - cpus, - pool: CpuPool::new(cpus), - } - } - - pub fn new() -> Worker { - Self::new_with_cpus(num_cpus::get()) - } - - pub fn log_num_cpus(&self) -> u32 { - log2_floor(self.cpus) - } - - pub fn compute(&self, f: F) -> WorkerFuture - where - F: FnOnce() -> R + Send + 'static, - R: IntoFuture + 'static, - R::Future: Send + 'static, - R::Item: Send + 'static, - R::Error: Send + 'static, - { - WorkerFuture { - future: self.pool.spawn_fn(f), - } - } - - pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R - where - F: FnOnce(&Scope<'a>, usize) -> R, - { - let chunk_size = if elements < self.cpus { - 1 - } else { - elements / self.cpus - }; - - // TODO: Handle case where threads fail - crossbeam::scope(|scope| f(scope, chunk_size)) - .expect("Threads aren't allowed to fail yet") - } - } - - pub struct WorkerFuture { - future: CpuFuture, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - self.future.poll() - } - } - - fn log2_floor(num: usize) -> u32 { - assert!(num > 0); - - let mut pow = 0; - - while (1 << (pow + 1)) <= num { - pow += 1; - } - - pow - } - - #[test] - fn test_log2_floor() { - assert_eq!(log2_floor(1), 0); - assert_eq!(log2_floor(2), 1); - assert_eq!(log2_floor(3), 1); - assert_eq!(log2_floor(4), 2); - assert_eq!(log2_floor(5), 2); - assert_eq!(log2_floor(6), 2); - assert_eq!(log2_floor(7), 2); - assert_eq!(log2_floor(8), 3); - } -} - -#[cfg(not(feature = "multicore"))] -mod implementation { - use futures::{future, Future, IntoFuture, Poll}; - - #[derive(Clone)] - pub struct Worker; - - impl Worker { - pub fn new() -> Worker { - Worker - } - - pub fn log_num_cpus(&self) -> u32 { - 0 - } - - pub fn compute(&self, f: F) -> R::Future - where - F: FnOnce() -> R + Send + 'static, - R: IntoFuture + 'static, - R::Future: Send + 'static, - R::Item: Send + 'static, - R::Error: Send + 'static, - { - f().into_future() - } - - pub fn scope(&self, elements: usize, f: F) -> R - where - F: FnOnce(&DummyScope, usize) -> R, - { - f(&DummyScope, elements) - } - } - - pub struct WorkerFuture { - future: future::FutureResult, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - self.future.poll() - } - } - - pub struct DummyScope; - - impl DummyScope { - pub fn spawn(&self, f: F) { - f(self); - } - } -} - -pub use self::implementation::*; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs deleted file mode 100644 index b70a2ea643..0000000000 --- a/bellman/src/multiexp.rs +++ /dev/null @@ -1,340 +0,0 @@ -use super::multicore::Worker; -use bit_vec::{self, BitVec}; -use ff::{Endianness, Field, PrimeField}; -use futures::Future; -use group::prime::{PrimeCurve, PrimeCurveAffine}; -use std::io; -use std::iter; -use std::ops::AddAssign; -use std::sync::Arc; - -use super::SynthesisError; - -/// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { - type Source: Source; - - fn new(self) -> Self::Source; -} - -/// A source of bases, like an iterator. -pub trait Source { - fn next(&mut self) -> Result<&G, SynthesisError>; - - /// Skips `amt` elements from the source, avoiding deserialization. - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; -} - -pub trait AddAssignFromSource: PrimeCurve { - /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_from_source::Affine>>( - &mut self, - source: &mut S, - ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); - Ok(()) - } -} -impl AddAssignFromSource for G where G: PrimeCurve {} - -impl SourceBuilder for (Arc>, usize) { - type Source = (Arc>, usize); - - fn new(self) -> (Arc>, usize) { - (self.0.clone(), self.1) - } -} - -impl Source for (Arc>, usize) { - fn next(&mut self) -> Result<&G, SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "expected more bases from source", - ) - .into()); - } - - if self.0[self.1].is_identity().into() { - return Err(SynthesisError::UnexpectedIdentity); - } - - let ret = &self.0[self.1]; - self.1 += 1; - - Ok(ret) - } - - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "expected more bases from source", - ) - .into()); - } - - self.1 += amt; - - Ok(()) - } -} - -pub trait QueryDensity { - /// Returns whether the base exists. - type Iter: Iterator; - - fn iter(self) -> Self::Iter; - fn get_query_size(self) -> Option; -} - -#[derive(Clone)] -pub struct FullDensity; - -impl AsRef for FullDensity { - fn as_ref(&self) -> &FullDensity { - self - } -} - -impl<'a> QueryDensity for &'a FullDensity { - type Iter = iter::Repeat; - - fn iter(self) -> Self::Iter { - iter::repeat(true) - } - - fn get_query_size(self) -> Option { - None - } -} - -pub struct DensityTracker { - bv: BitVec, - total_density: usize, -} - -impl<'a> QueryDensity for &'a DensityTracker { - type Iter = bit_vec::Iter<'a>; - - fn iter(self) -> Self::Iter { - self.bv.iter() - } - - fn get_query_size(self) -> Option { - Some(self.bv.len()) - } -} - -impl DensityTracker { - pub fn new() -> DensityTracker { - DensityTracker { - bv: BitVec::new(), - total_density: 0, - } - } - - pub fn add_element(&mut self) { - self.bv.push(false); - } - - pub fn inc(&mut self, idx: usize) { - if !self.bv.get(idx).unwrap() { - self.bv.set(idx, true); - self.total_density += 1; - } - } - - pub fn get_total_density(&self) -> usize { - self.total_density - } -} - -fn multiexp_inner( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc>, - mut skip: u32, - c: u32, - handle_trivial: bool, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: PrimeCurve, - S: SourceBuilder<::Affine>, -{ - // Perform this region of the multiexp - let this = { - let bases = bases.clone(); - let exponents = exponents.clone(); - let density_map = density_map.clone(); - - pool.compute(move || { - // Accumulate the result - let mut acc = G::identity(); - - // Build a source for the bases - let mut bases = bases.new(); - - // Create space for the buckets - let mut buckets = vec![G::identity(); (1 << c) - 1]; - - let one = G::Scalar::one(); - - // Sort the bases into buckets - for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { - if density { - if exp.is_zero() { - bases.skip(1)?; - } else if exp == one { - if handle_trivial { - acc.add_assign_from_source(&mut bases)?; - } else { - bases.skip(1)?; - } - } else { - let mut exp = exp.to_repr(); - ::ReprEndianness::toggle_little_endian(&mut exp); - - let exp = exp - .as_ref() - .into_iter() - .map(|b| (0..8).map(move |i| (b >> i) & 1u8)) - .flatten() - .skip(skip as usize) - .take(c as usize) - .enumerate() - .fold(0u64, |acc, (i, b)| acc + ((b as u64) << i)); - - if exp != 0 { - (&mut buckets[(exp - 1) as usize]) - .add_assign_from_source(&mut bases)?; - } else { - bases.skip(1)?; - } - } - } - } - - // Summation by parts - // e.g. 3a + 2b + 1c = a + - // (a) + b + - // ((a) + b) + c - let mut running_sum = G::identity(); - for exp in buckets.into_iter().rev() { - running_sum.add_assign(&exp); - acc.add_assign(&running_sum); - } - - Ok(acc) - }) - }; - - skip += c; - - if skip >= G::Scalar::NUM_BITS { - // There isn't another region. - Box::new(this) - } else { - // There's another region more significant. Calculate and join it with - // this region recursively. - Box::new( - this.join(multiexp_inner( - pool, - bases, - density_map, - exponents, - skip, - c, - false, - )) - .map(move |(this, mut higher): (_, G)| { - for _ in 0..c { - higher = higher.double(); - } - - higher.add_assign(&this); - - higher - }), - ) - } -} - -/// Perform multi-exponentiation. The caller is responsible for ensuring the -/// query size is the same as the number of exponents. -pub fn multiexp( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc>, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: PrimeCurve, - S: SourceBuilder<::Affine>, -{ - let c = if exponents.len() < 32 { - 3u32 - } else { - (f64::from(exponents.len() as u32)).ln().ceil() as u32 - }; - - if let Some(query_size) = density_map.as_ref().get_query_size() { - // If the density map has a known query size, it should not be - // inconsistent with the number of exponents. - - assert!(query_size == exponents.len()); - } - - multiexp_inner(pool, bases, density_map, exponents, 0, c, true) -} - -#[cfg(feature = "pairing")] -#[test] -fn test_with_bls12() { - fn naive_multiexp( - bases: Arc::Affine>>, - exponents: Arc>, - ) -> G { - assert_eq!(bases.len(), exponents.len()); - - let mut acc = G::identity(); - - for (base, exp) in bases.iter().zip(exponents.iter()) { - AddAssign::<&G>::add_assign(&mut acc, &(*base * *exp)); - } - - acc - } - - use bls12_381::{Bls12, Scalar}; - use group::{Curve, Group}; - use pairing::Engine; - use rand; - - const SAMPLES: usize = 1 << 14; - - let rng = &mut rand::thread_rng(); - let v = Arc::new( - (0..SAMPLES) - .map(|_| Scalar::random(rng)) - .collect::>(), - ); - let g = Arc::new( - (0..SAMPLES) - .map(|_| ::G1::random(rng).to_affine()) - .collect::>(), - ); - - let naive: ::G1 = naive_multiexp(g.clone(), v.clone()); - - let pool = Worker::new(); - - let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap(); - - assert_eq!(naive, fast); -} diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs deleted file mode 100644 index e8cb9219bc..0000000000 --- a/bellman/tests/mimc.rs +++ /dev/null @@ -1,226 +0,0 @@ -// For randomness (during paramgen and proof generation) -use rand::thread_rng; - -// For benchmarking -use std::time::{Duration, Instant}; - -// Bring in some tools for using finite fiels -use ff::{Field, PrimeField}; - -// We're going to use the BLS12-381 pairing-friendly elliptic curve. -use bls12_381::{Bls12, Scalar}; - -// We'll use these interfaces to construct our circuit. -use bellman::{Circuit, ConstraintSystem, SynthesisError}; - -// We're going to use the Groth16 proving system. -use bellman::groth16::{ - create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof, -}; - -const MIMC_ROUNDS: usize = 322; - -/// This is an implementation of MiMC, specifically a -/// variant named `LongsightF322p3` for BLS12-381. -/// See http://eprint.iacr.org/2016/492 for more -/// information about this construction. -/// -/// ``` -/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { -/// for i from 0 up to 321 { -/// xL, xR := xR + (xL + Ci)^3, xL -/// } -/// return xL -/// } -/// ``` -fn mimc(mut xl: Scalar, mut xr: Scalar, constants: &[Scalar]) -> Scalar { - assert_eq!(constants.len(), MIMC_ROUNDS); - - for i in 0..MIMC_ROUNDS { - let mut tmp1 = xl; - tmp1.add_assign(&constants[i]); - let mut tmp2 = tmp1.square(); - tmp2.mul_assign(&tmp1); - tmp2.add_assign(&xr); - xr = xl; - xl = tmp2; - } - - xl -} - -/// This is our demo circuit for proving knowledge of the -/// preimage of a MiMC hash invocation. -struct MiMCDemo<'a, Scalar: PrimeField> { - xl: Option, - xr: Option, - constants: &'a [Scalar], -} - -/// Our demo circuit implements this `Circuit` trait which -/// is used during paramgen and proving in order to -/// synthesize the constraint system. -impl<'a, Scalar: PrimeField> Circuit for MiMCDemo<'a, Scalar> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - assert_eq!(self.constants.len(), MIMC_ROUNDS); - - // Allocate the first component of the preimage. - let mut xl_value = self.xl; - let mut xl = cs.alloc( - || "preimage xl", - || xl_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - // Allocate the second component of the preimage. - let mut xr_value = self.xr; - let mut xr = cs.alloc( - || "preimage xr", - || xr_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - for i in 0..MIMC_ROUNDS { - // xL, xR := xR + (xL + Ci)^3, xL - let cs = &mut cs.namespace(|| format!("round {}", i)); - - // tmp = (xL + Ci)^2 - let tmp_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.square() - }); - let tmp = cs.alloc( - || "tmp", - || tmp_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "tmp = (xL + Ci)^2", - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + tmp, - ); - - // new_xL = xR + (xL + Ci)^3 - // new_xL = xR + tmp * (xL + Ci) - // new_xL - xR = tmp * (xL + Ci) - let new_xl_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.mul_assign(&tmp_value.unwrap()); - e.add_assign(&xr_value.unwrap()); - e - }); - - let new_xl = if i == (MIMC_ROUNDS - 1) { - // This is the last round, xL is our image and so - // we allocate a public input. - cs.alloc_input( - || "image", - || new_xl_value.ok_or(SynthesisError::AssignmentMissing), - )? - } else { - cs.alloc( - || "new_xl", - || new_xl_value.ok_or(SynthesisError::AssignmentMissing), - )? - }; - - cs.enforce( - || "new_xL = xR + (xL + Ci)^3", - |lc| lc + tmp, - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + new_xl - xr, - ); - - // xR = xL - xr = xl; - xr_value = xl_value; - - // xL = new_xL - xl = new_xl; - xl_value = new_xl_value; - } - - Ok(()) - } -} - -#[test] -fn test_mimc() { - // This may not be cryptographically safe, use - // `OsRng` (for example) in production software. - let rng = &mut thread_rng(); - - // Generate the MiMC round constants - let constants = (0..MIMC_ROUNDS) - .map(|_| Scalar::random(rng)) - .collect::>(); - - println!("Creating parameters..."); - - // Create parameters for our circuit - let params = { - let c = MiMCDemo { - xl: None, - xr: None, - constants: &constants, - }; - - generate_random_parameters::(c, rng).unwrap() - }; - - // Prepare the verification key (for proof verification) - let pvk = prepare_verifying_key(¶ms.vk); - - println!("Creating proofs..."); - - // Let's benchmark stuff! - const SAMPLES: u32 = 50; - let mut total_proving = Duration::new(0, 0); - let mut total_verifying = Duration::new(0, 0); - - // Just a place to put the proof data, so we can - // benchmark deserialization. - let mut proof_vec = vec![]; - - for _ in 0..SAMPLES { - // Generate a random preimage and compute the image - let xl = Scalar::random(rng); - let xr = Scalar::random(rng); - let image = mimc(xl, xr, &constants); - - proof_vec.truncate(0); - - let start = Instant::now(); - { - // Create an instance of our circuit (with the - // witness) - let c = MiMCDemo { - xl: Some(xl), - xr: Some(xr), - constants: &constants, - }; - - // Create a groth16 proof with our parameters. - let proof = create_random_proof(c, ¶ms, rng).unwrap(); - - proof.write(&mut proof_vec).unwrap(); - } - - total_proving += start.elapsed(); - - let start = Instant::now(); - let proof = Proof::read(&proof_vec[..]).unwrap(); - // Check the proof - assert!(verify_proof(&pvk, &proof, &[image]).is_ok()); - total_verifying += start.elapsed(); - } - let proving_avg = total_proving / SAMPLES; - let proving_avg = - proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); - - let verifying_avg = total_verifying / SAMPLES; - let verifying_avg = - verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); - - println!("Average proving time: {:?} seconds", proving_avg); - println!("Average verifying time: {:?} seconds", verifying_avg); -} diff --git a/bls12_381/.github/workflows/ci.yml b/bls12_381/.github/workflows/ci.yml deleted file mode 100644 index 1388989f0a..0000000000 --- a/bls12_381/.github/workflows/ci.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: CI checks - -on: [push, pull_request] - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - # Ensure all code has been formatted with rustfmt - - run: rustup component add rustfmt - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check --color always - - test: - name: Test on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build tests - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --release --tests --features endo - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release --features endo - - name: Build tests (no endomorphism) - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --release --tests - - name: Run tests (no endomorphism) - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release - - no-std: - name: Check no-std compatibility - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - run: rustup target add thumbv6m-none-eabi - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings - - doc-links: - name: Nightly lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - # Ensure intra-documentation links all resolve correctly - # Requires #![deny(intra_doc_link_resolution_failure)] in crate. - - name: Check intra-doc links - uses: actions-rs/cargo@v1 - with: - command: doc - args: --document-private-items diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore deleted file mode 100644 index 2f88dbac54..0000000000 --- a/bls12_381/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock \ No newline at end of file diff --git a/bls12_381/COPYRIGHT b/bls12_381/COPYRIGHT deleted file mode 100644 index 7764b866e0..0000000000 --- a/bls12_381/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "bls12_381" library are retained by their contributors. No -copyright assignment is required to contribute to the "bls12_381" library. - -The "bls12_381" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml deleted file mode 100644 index 4411401425..0000000000 --- a/bls12_381/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction" -documentation = "https://docs.rs/bls12_381/" -homepage = "https://github.com/zkcrypto/bls12_381" -license = "MIT/Apache-2.0" -name = "bls12_381" -repository = "https://github.com/zkcrypto/bls12_381" -version = "0.2.0" -edition = "2018" - -[package.metadata.docs.rs] -rustdoc-args = [ "--html-in-header", "katex-header.html" ] - -[dev-dependencies] -criterion = "0.3" - -[[bench]] -name = "groups" -harness = false -required-features = ["groups"] - -[dependencies.byteorder] -version = "1" -default-features = false - -[dependencies.ff] -path = "../ff" -version = "0.7" -default-features = false - -[dependencies.group] -path = "../group" -version = "0.7" -default-features = false -optional = true - -[dependencies.pairing] -path = "../pairing" -version = "0.17" -optional = true - -[dependencies.rand_core] -version = "0.5" -default-features = false - -[dependencies.subtle] -version = "2.2.1" -default-features = false - -[features] -default = ["groups", "pairings", "alloc"] -groups = ["group"] -pairings = ["groups", "pairing"] -alloc = [] -nightly = ["subtle/nightly"] - -# GLV patents US7110538B2 and US7995752B2 expire in September 2020. -endo = [] diff --git a/bls12_381/LICENSE-APACHE b/bls12_381/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/bls12_381/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/bls12_381/LICENSE-MIT b/bls12_381/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/bls12_381/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/bls12_381/README.md b/bls12_381/README.md deleted file mode 100644 index 70a238e50a..0000000000 --- a/bls12_381/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) # - -This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction. - -* **This implementation has not been reviewed or audited. Use at your own risk.** -* This implementation targets Rust `1.36` or later. -* This implementation does not require the Rust standard library. -* All operations are constant time unless explicitly noted. - -## Features - -* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT. -* `pairings` (on by default): Enables some APIs for performing pairings. -* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. -* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. -* `endo`: Enables optimizations that leverage curve endomorphisms, which may run foul of patents US7110538B2 and US7995752B2 set to expire in September 2020. - -## [Documentation](https://docs.rs/bls12_381) - -## Curve Description - -BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with... - -* z = `-0xd201000000010000` -* p = (z - 1)2(z4 - z2 + 1) / 3 + z - * = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` -* q = z4 - z2 + 1 - * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extension field Fp2 is defined as Fp(u) / (u2 + 1). - -BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. - -### Curve Security - -Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. - -In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yield a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. - -There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) - -### Alternative Curves - -Applications may wish to exchange pairing performance and/or G2 performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe. - -## Acknowledgements - -Please see `Cargo.toml` for a list of primary authors of this codebase. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/bls12_381/RELEASES.md b/bls12_381/RELEASES.md deleted file mode 100644 index 5f314c4379..0000000000 --- a/bls12_381/RELEASES.md +++ /dev/null @@ -1,44 +0,0 @@ -# 0.2.0 - -This release adds implementations of the `ff`, `group`, and `pairing` traits (with the -latter two being gated by the `groups` and `pairings` feature flags respectively). -Additional trait implementations (for standard traits) have been added where the `ff`, -`group`, and `pairing` trait bounds require them. - -## Added -* `bls12_381::Bls12`, a `pairing::Engine` for BLS12-381 pairing operations. It implements - the following traits: - * `pairing::{Engine, MultiMillerLoop}` -* New trait implementations for `bls12_381::G1Projective`: - * `group::{Curve, Group, GroupEncoding, WnafGroup}` - * `group::prime::{PrimeCurve, PrimeGroup}` -* New trait implementations for `bls12_381::G1Affine`: - * `group::{GroupEncoding, UncompressedEncoding}` - * `group::prime::PrimeCurveAffine` - * `pairing::PairingCurveAffine` -* New trait implementations for `bls12_381::G2Projective`: - * `group::{Curve, Group, GroupEncoding, WnafGroup}` - * `group::prime::{PrimeCurve, PrimeGroup}` -* New trait implementations for `bls12_381::G2Affine`: - * `group::{GroupEncoding, UncompressedEncoding}` - * `group::prime::PrimeCurveAffine` - * `pairing::PairingCurveAffine` -* New trait implementations for `bls12_381::Gt`: - * `group::Group` -* New trait implementations for `bls12_381::MillerLoopResult`: - * `pairing::MillerLoopResult` -* New trait implementations for `bls12_381::Scalar`: - * `ff::{Field, PrimeField}` - -# 0.1.1 - -Added `clear_cofactor` methods to `G1Projective` and `G2Projective`. If the crate feature `endo` -is enabled the G2 cofactor clearing will use the curve endomorphism technique described by -[Budroni-Pintore](https://ia.cr/2017/419). If the crate feature `endo` is _not_ enabled then -the code will simulate the effects of the Budroni-Pintore cofactor clearing in order to keep -the API consistent. In September 2020, when patents US7110538B2 and US7995752B2 expire, the -endo feature will be made default. However, for now it must be explicitly enabled. - -# 0.1.0 - -Initial release. diff --git a/bls12_381/benches/groups.rs b/bls12_381/benches/groups.rs deleted file mode 100644 index 87c80d029c..0000000000 --- a/bls12_381/benches/groups.rs +++ /dev/null @@ -1,170 +0,0 @@ -#[macro_use] -extern crate criterion; - -extern crate bls12_381; -use bls12_381::*; - -use criterion::{black_box, Criterion}; - -fn criterion_benchmark(c: &mut Criterion) { - // Pairings - { - let g = G1Affine::generator(); - let h = G2Affine::generator(); - c.bench_function("full pairing", move |b| { - b.iter(|| pairing(black_box(&g), black_box(&h))) - }); - c.bench_function("G2 preparation for pairing", move |b| { - b.iter(|| G2Prepared::from(h)) - }); - let prep = G2Prepared::from(h); - c.bench_function("miller loop for pairing", move |b| { - b.iter(|| multi_miller_loop(&[(&g, &prep)])) - }); - let prep = G2Prepared::from(h); - let r = multi_miller_loop(&[(&g, &prep)]); - c.bench_function("final exponentiation for pairing", move |b| { - b.iter(|| r.final_exponentiation()) - }); - } - // G1Affine - { - let name = "G1Affine"; - let a = G1Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - let compressed = [0u8; 48]; - let uncompressed = [0u8; 96]; - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} subgroup check", name), move |b| { - b.iter(|| black_box(a).is_torsion_free()) - }); - c.bench_function( - &format!("{} deserialize compressed point", name), - move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))), - ); - c.bench_function( - &format!("{} deserialize uncompressed point", name), - move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))), - ); - } - - // G1Projective - { - let name = "G1Projective"; - let a = G1Projective::generator(); - let a_affine = G1Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - - const N: usize = 10000; - let v = vec![G1Projective::generator(); N]; - let mut q = vec![G1Affine::identity(); N]; - - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} to affine", name), move |b| { - b.iter(|| G1Affine::from(black_box(a))) - }); - c.bench_function(&format!("{} doubling", name), move |b| { - b.iter(|| black_box(a).double()) - }); - c.bench_function(&format!("{} addition", name), move |b| { - b.iter(|| black_box(a).add(&a)) - }); - c.bench_function(&format!("{} mixed addition", name), move |b| { - b.iter(|| black_box(a).add_mixed(&a_affine)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { - b.iter(|| { - G1Projective::batch_normalize(black_box(&v), black_box(&mut q)); - black_box(&q)[0] - }) - }); - } - - // G2Affine - { - let name = "G2Affine"; - let a = G2Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - let compressed = [0u8; 96]; - let uncompressed = [0u8; 192]; - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} subgroup check", name), move |b| { - b.iter(|| black_box(a).is_torsion_free()) - }); - c.bench_function( - &format!("{} deserialize compressed point", name), - move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))), - ); - c.bench_function( - &format!("{} deserialize uncompressed point", name), - move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))), - ); - } - - // G2Projective - { - let name = "G2Projective"; - let a = G2Projective::generator(); - let a_affine = G2Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - - const N: usize = 10000; - let v = vec![G2Projective::generator(); N]; - let mut q = vec![G2Affine::identity(); N]; - - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} to affine", name), move |b| { - b.iter(|| G2Affine::from(black_box(a))) - }); - c.bench_function(&format!("{} doubling", name), move |b| { - b.iter(|| black_box(a).double()) - }); - c.bench_function(&format!("{} addition", name), move |b| { - b.iter(|| black_box(a).add(&a)) - }); - c.bench_function(&format!("{} mixed addition", name), move |b| { - b.iter(|| black_box(a).add_mixed(&a_affine)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { - b.iter(|| { - G2Projective::batch_normalize(black_box(&v), black_box(&mut q)); - black_box(&q)[0] - }) - }); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/bls12_381/katex-header.html b/bls12_381/katex-header.html deleted file mode 100644 index 98e85904fa..0000000000 --- a/bls12_381/katex-header.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/bls12_381/rust-toolchain b/bls12_381/rust-toolchain deleted file mode 100644 index d70132e1de..0000000000 --- a/bls12_381/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.36.0 \ No newline at end of file diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs deleted file mode 100644 index 3369554515..0000000000 --- a/bls12_381/src/fp.rs +++ /dev/null @@ -1,916 +0,0 @@ -//! This module provides an implementation of the BLS12-381 base field `GF(p)` -//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` - -use core::convert::TryFrom; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -// The internal representation of this type is six 64-bit unsigned -// integers in little-endian order. `Fp` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. -#[derive(Copy, Clone)] -pub struct Fp([u64; 6]); - -impl fmt::Debug for Fp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl Default for Fp { - fn default() -> Self { - Fp::zero() - } -} - -impl ConstantTimeEq for Fp { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - & self.0[4].ct_eq(&other.0[4]) - & self.0[5].ct_eq(&other.0[5]) - } -} - -impl Eq for Fp {} -impl PartialEq for Fp { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Fp { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - u64::conditional_select(&a.0[4], &b.0[4], choice), - u64::conditional_select(&a.0[5], &b.0[5], choice), - ]) - } -} - -/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -const MODULUS: [u64; 6] = [ - 0xb9fe_ffff_ffff_aaab, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a, -]; - -/// INV = -(p^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x89f3_fffc_fffc_fffd; - -/// R = 2^384 mod p -const R: Fp = Fp([ - 0x7609_0000_0002_fffd, - 0xebf4_000b_c40c_0002, - 0x5f48_9857_53c7_58ba, - 0x77ce_5853_7052_5745, - 0x5c07_1a97_a256_ec6d, - 0x15f6_5ec3_fa80_e493, -]); - -/// R2 = 2^(384*2) mod p -const R2: Fp = Fp([ - 0xf4df_1f34_1c34_1746, - 0x0a76_e6a6_09d1_04f1, - 0x8de5_476c_4c95_b6d5, - 0x67eb_88a9_939d_83c0, - 0x9a79_3e85_b519_952d, - 0x1198_8fe5_92ca_e3aa, -]); - -/// R3 = 2^(384*3) mod p -const R3: Fp = Fp([ - 0xed48_ac6b_d94c_a1e0, - 0x315f_831e_03a7_adf8, - 0x9a53_352a_615e_29dd, - 0x34c0_4e5e_921e_1761, - 0x2512_d435_6572_4728, - 0x0aa6_3460_9175_5d4d, -]); - -impl<'a> Neg for &'a Fp { - type Output = Fp; - - #[inline] - fn neg(self) -> Fp { - self.neg() - } -} - -impl Neg for Fp { - type Output = Fp; - - #[inline] - fn neg(self) -> Fp { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn sub(self, rhs: &'b Fp) -> Fp { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn add(self, rhs: &'b Fp) -> Fp { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn mul(self, rhs: &'b Fp) -> Fp { - self.mul(rhs) - } -} - -impl_binops_additive!(Fp, Fp); -impl_binops_multiplicative!(Fp, Fp); - -impl Fp { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Fp { - Fp([0, 0, 0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Fp { - R - } - - pub fn is_zero(&self) -> Choice { - self.ct_eq(&Fp::zero()) - } - - /// Attempts to convert a big-endian byte representation of - /// a scalar into an `Fp`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { - let mut tmp = Fp([0, 0, 0, 0, 0, 0]); - - tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); - tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); - tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); - let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); - let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Fp` into a byte representation in - /// big-endian byte order. - pub fn to_bytes(&self) -> [u8; 48] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fp::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, - ); - - let mut res = [0; 48]; - res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes()); - res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes()); - res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes()); - res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes()); - res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes()); - res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes()); - - res - } - - pub(crate) fn random(rng: &mut R) -> Fp { - let mut bytes = [0u8; 96]; - rng.fill_bytes(&mut bytes); - - // Parse the random bytes as a big-endian number, to match Fp encoding order. - Fp::from_u768([ - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[64..72]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[72..80]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[80..88]).unwrap()), - u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[88..96]).unwrap()), - ]) - } - - /// Reduces a big-endian 64-bit limb representation of a 768-bit number. - fn from_u768(limbs: [u64; 12]) -> Fp { - // We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits - // with the higher bits multiplied by 2^384. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^384 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 384-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^384 multiplied by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]); - let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Returns whether or not this element is strictly lexicographically - /// larger than its negation. - pub fn lexicographically_largest(&self) -> Choice { - // This can be determined by checking to see if the element is - // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 - // and there is no underflow, then the element must be larger than - // (p - 1) // 2. - - // First, because self is in Montgomery form we need to reduce it - let tmp = Fp::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, - ); - - let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0); - let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow); - let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow); - let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow); - let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow); - let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow); - - // If the element was smaller, the subtraction will underflow - // producing a borrow value of 0xffff...ffff, otherwise it will - // be zero. We create a Choice representing true if there was - // overflow (and so this element is not lexicographically larger - // than its negation) and then negate it. - - !Choice::from((borrow as u8) & 1) - } - - /// Constructs an element of `Fp` without checking that it is - /// canonical. - pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp { - Fp(v) - } - - /// Although this is labeled "vartime", it is only - /// variable time with respect to the exponent. It - /// is also not exposed in the public API. - pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res *= self; - } - } - } - res - } - - #[inline] - pub fn sqrt(&self) -> CtOption { - // We use Shank's method, as p = 3 (mod 4). This means - // we only need to exponentiate by (p+1)/4. This only - // works for elements that are actually quadratic residue, - // so we check that we got the correct result at the end. - - let sqrt = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaab, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); - - CtOption::new(sqrt, sqrt.square().ct_eq(self)) - } - - #[inline] - /// Computes the multiplicative inverse of this field - /// element, returning None in the case that this element - /// is zero. - pub fn invert(&self) -> CtOption { - // Exponentiate by p - 2 - let t = self.pow_vartime(&[ - 0xb9fe_ffff_ffff_aaa9, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a, - ]); - - CtOption::new(t, !self.is_zero()) - } - - #[inline] - const fn subtract_p(&self) -> Fp { - let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0); - let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow); - let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow); - let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow); - let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow); - let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask! - let r0 = (self.0[0] & borrow) | (r0 & !borrow); - let r1 = (self.0[1] & borrow) | (r1 & !borrow); - let r2 = (self.0[2] & borrow) | (r2 & !borrow); - let r3 = (self.0[3] & borrow) | (r3 & !borrow); - let r4 = (self.0[4] & borrow) | (r4 & !borrow); - let r5 = (self.0[5] & borrow) | (r5 & !borrow); - - Fp([r0, r1, r2, r3, r4, r5]) - } - - #[inline] - pub const fn add(&self, rhs: &Fp) -> Fp { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, carry) = adc(self.0[3], rhs.0[3], carry); - let (d4, carry) = adc(self.0[4], rhs.0[4], carry); - let (d5, _) = adc(self.0[5], rhs.0[5], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fp([d0, d1, d2, d3, d4, d5])).subtract_p() - } - - #[inline] - pub const fn neg(&self) -> Fp { - let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow); - let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow); - let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow); - let (d5, _) = sbb(MODULUS[5], self.0[5], borrow); - - // Let's use a mask if `self` was zero, which would mean - // the result of the subtraction is p. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0) - as u64) - .wrapping_sub(1); - - Fp([ - d0 & mask, - d1 & mask, - d2 & mask, - d3 & mask, - d4 & mask, - d5 & mask, - ]) - } - - #[inline] - pub const fn sub(&self, rhs: &Fp) -> Fp { - (&rhs.neg()).add(self) - } - - #[inline(always)] - const fn montgomery_reduce( - t0: u64, - t1: u64, - t2: u64, - t3: u64, - t4: u64, - t5: u64, - t6: u64, - t7: u64, - t8: u64, - t9: u64, - t10: u64, - t11: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = t0.wrapping_mul(INV); - let (_, carry) = mac(t0, k, MODULUS[0], 0); - let (r1, carry) = mac(t1, k, MODULUS[1], carry); - let (r2, carry) = mac(t2, k, MODULUS[2], carry); - let (r3, carry) = mac(t3, k, MODULUS[3], carry); - let (r4, carry) = mac(t4, k, MODULUS[4], carry); - let (r5, carry) = mac(t5, k, MODULUS[5], carry); - let (r6, r7) = adc(t6, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS[0], 0); - let (r2, carry) = mac(r2, k, MODULUS[1], carry); - let (r3, carry) = mac(r3, k, MODULUS[2], carry); - let (r4, carry) = mac(r4, k, MODULUS[3], carry); - let (r5, carry) = mac(r5, k, MODULUS[4], carry); - let (r6, carry) = mac(r6, k, MODULUS[5], carry); - let (r7, r8) = adc(t7, r7, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS[0], 0); - let (r3, carry) = mac(r3, k, MODULUS[1], carry); - let (r4, carry) = mac(r4, k, MODULUS[2], carry); - let (r5, carry) = mac(r5, k, MODULUS[3], carry); - let (r6, carry) = mac(r6, k, MODULUS[4], carry); - let (r7, carry) = mac(r7, k, MODULUS[5], carry); - let (r8, r9) = adc(t8, r8, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS[0], 0); - let (r4, carry) = mac(r4, k, MODULUS[1], carry); - let (r5, carry) = mac(r5, k, MODULUS[2], carry); - let (r6, carry) = mac(r6, k, MODULUS[3], carry); - let (r7, carry) = mac(r7, k, MODULUS[4], carry); - let (r8, carry) = mac(r8, k, MODULUS[5], carry); - let (r9, r10) = adc(t9, r9, carry); - - let k = r4.wrapping_mul(INV); - let (_, carry) = mac(r4, k, MODULUS[0], 0); - let (r5, carry) = mac(r5, k, MODULUS[1], carry); - let (r6, carry) = mac(r6, k, MODULUS[2], carry); - let (r7, carry) = mac(r7, k, MODULUS[3], carry); - let (r8, carry) = mac(r8, k, MODULUS[4], carry); - let (r9, carry) = mac(r9, k, MODULUS[5], carry); - let (r10, r11) = adc(t10, r10, carry); - - let k = r5.wrapping_mul(INV); - let (_, carry) = mac(r5, k, MODULUS[0], 0); - let (r6, carry) = mac(r6, k, MODULUS[1], carry); - let (r7, carry) = mac(r7, k, MODULUS[2], carry); - let (r8, carry) = mac(r8, k, MODULUS[3], carry); - let (r9, carry) = mac(r9, k, MODULUS[4], carry); - let (r10, carry) = mac(r10, k, MODULUS[5], carry); - let (r11, _) = adc(t11, r11, carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fp([r6, r7, r8, r9, r10, r11])).subtract_p() - } - - #[inline] - pub const fn mul(&self, rhs: &Fp) -> Fp { - let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry); - let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry); - let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry); - - let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0); - let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry); - let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry); - let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry); - let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry); - let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry); - - let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0); - let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry); - let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry); - let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry); - let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry); - let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry); - - let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0); - let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry); - let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry); - let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry); - let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry); - let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry); - - let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0); - let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry); - let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry); - let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry); - let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry); - let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry); - - let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0); - let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry); - let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry); - let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry); - let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry); - let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry); - - Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Self { - let (t1, carry) = mac(0, self.0[0], self.0[1], 0); - let (t2, carry) = mac(0, self.0[0], self.0[2], carry); - let (t3, carry) = mac(0, self.0[0], self.0[3], carry); - let (t4, carry) = mac(0, self.0[0], self.0[4], carry); - let (t5, t6) = mac(0, self.0[0], self.0[5], carry); - - let (t3, carry) = mac(t3, self.0[1], self.0[2], 0); - let (t4, carry) = mac(t4, self.0[1], self.0[3], carry); - let (t5, carry) = mac(t5, self.0[1], self.0[4], carry); - let (t6, t7) = mac(t6, self.0[1], self.0[5], carry); - - let (t5, carry) = mac(t5, self.0[2], self.0[3], 0); - let (t6, carry) = mac(t6, self.0[2], self.0[4], carry); - let (t7, t8) = mac(t7, self.0[2], self.0[5], carry); - - let (t7, carry) = mac(t7, self.0[3], self.0[4], 0); - let (t8, t9) = mac(t8, self.0[3], self.0[5], carry); - - let (t9, t10) = mac(t9, self.0[4], self.0[5], 0); - - let t11 = t10 >> 63; - let t10 = (t10 << 1) | (t9 >> 63); - let t9 = (t9 << 1) | (t8 >> 63); - let t8 = (t8 << 1) | (t7 >> 63); - let t7 = (t7 << 1) | (t6 >> 63); - let t6 = (t6 << 1) | (t5 >> 63); - let t5 = (t5 << 1) | (t4 >> 63); - let t4 = (t4 << 1) | (t3 >> 63); - let t3 = (t3 << 1) | (t2 >> 63); - let t2 = (t2 << 1) | (t1 >> 63); - let t1 = t1 << 1; - - let (t0, carry) = mac(0, self.0[0], self.0[0], 0); - let (t1, carry) = adc(t1, 0, carry); - let (t2, carry) = mac(t2, self.0[1], self.0[1], carry); - let (t3, carry) = adc(t3, 0, carry); - let (t4, carry) = mac(t4, self.0[2], self.0[2], carry); - let (t5, carry) = adc(t5, 0, carry); - let (t6, carry) = mac(t6, self.0[3], self.0[3], carry); - let (t7, carry) = adc(t7, 0, carry); - let (t8, carry) = mac(t8, self.0[4], self.0[4], carry); - let (t9, carry) = adc(t9, 0, carry); - let (t10, carry) = mac(t10, self.0[5], self.0[5], carry); - let (t11, _) = adc(t11, 0, carry); - - Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) - } -} - -#[test] -fn test_conditional_selection() { - let a = Fp([1, 2, 3, 4, 5, 6]); - let b = Fp([7, 8, 9, 10, 11, 12]); - - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_equality() { - fn is_equal(a: &Fp, b: &Fp) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); - - assert_eq!(eq, bool::from(ct_eq)); - - eq - } - - assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - - assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); -} - -#[test] -fn test_squaring() { - let a = Fp([ - 0xd215_d276_8e83_191b, - 0x5085_d80f_8fb2_8261, - 0xce9a_032d_df39_3a56, - 0x3e9c_4fff_2ca0_c4bb, - 0x6436_b6f7_f4d9_5dfb, - 0x1060_6628_ad4a_4d90, - ]); - let b = Fp([ - 0x33d9_c42a_3cb3_e235, - 0xdad1_1a09_4c4c_d455, - 0xa2f1_44bd_729a_aeba, - 0xd415_0932_be9f_feac, - 0xe27b_c7c4_7d44_ee50, - 0x14b6_a78d_3ec7_a560, - ]); - - assert_eq!(a.square(), b); -} - -#[test] -fn test_multiplication() { - let a = Fp([ - 0x0397_a383_2017_0cd4, - 0x734c_1b2c_9e76_1d30, - 0x5ed2_55ad_9a48_beb5, - 0x095a_3c6b_22a7_fcfc, - 0x2294_ce75_d4e2_6a27, - 0x1333_8bd8_7001_1ebb, - ]); - let b = Fp([ - 0xb9c3_c7c5_b119_6af7, - 0x2580_e208_6ce3_35c1, - 0xf49a_ed3d_8a57_ef42, - 0x41f2_81e4_9846_e878, - 0xe076_2346_c384_52ce, - 0x0652_e893_26e5_7dc0, - ]); - let c = Fp([ - 0xf96e_f3d7_11ab_5355, - 0xe8d4_59ea_00f1_48dd, - 0x53f7_354a_5f00_fa78, - 0x9e34_a4f3_125c_5f83, - 0x3fbe_0c47_ca74_c19e, - 0x01b0_6a8b_bd4a_dfe4, - ]); - - assert_eq!(a * b, c); -} - -#[test] -fn test_addition() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091, - ]); - let c = Fp([ - 0x3934_42cc_b58b_b327, - 0x1092_685f_3bd5_47e3, - 0x3382_252c_ab6a_c4c9, - 0xf946_94cb_7688_7f55, - 0x4b21_5e90_93a5_e071, - 0x0d56_e30f_34f5_f853, - ]); - - assert_eq!(a + b, c); -} - -#[test] -fn test_subtraction() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091, - ]); - let c = Fp([ - 0x6d8d_33e6_3b43_4d3d, - 0xeb12_82fd_b766_dd39, - 0x8534_7bb6_f133_d6d5, - 0xa21d_aa5a_9892_f727, - 0x3b25_6cfb_3ad8_ae23, - 0x155d_7199_de7f_8464, - ]); - - assert_eq!(a - b, c); -} - -#[test] -fn test_negation() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x669e_44a6_8798_2a79, - 0xa0d9_8a50_37b5_ed71, - 0x0ad5_822f_2861_a854, - 0x96c5_2bf1_ebf7_5781, - 0x87f8_41f0_5c0c_658c, - 0x08a6_e795_afc5_283e, - ]); - - assert_eq!(-a, b); -} - -#[test] -fn test_debug() { - assert_eq!( - format!( - "{:?}", - Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]) - ), - "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" - ); -} - -#[test] -fn test_from_bytes() { - let mut a = Fp([ - 0xdc90_6d9b_e3f9_5dc8, - 0x8755_caf7_4596_91a1, - 0xcff1_a7f4_e958_3ab3, - 0x9b43_821f_849e_2284, - 0xf575_54f3_a297_4f3f, - 0x085d_bea8_4ed4_7f79, - ]); - - for _ in 0..100 { - a = a.square(); - let tmp = a.to_bytes(); - let b = Fp::from_bytes(&tmp).unwrap(); - - assert_eq!(a, b); - } - - assert_eq!( - -Fp::one(), - Fp::from_bytes(&[ - 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .unwrap() - ); - - assert!(bool::from( - Fp::from_bytes(&[ - 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .is_none() - )); - - assert!(bool::from(Fp::from_bytes(&[0xff; 48]).is_none())); -} - -#[test] -fn test_sqrt() { - // a = 4 - let a = Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]); - - assert_eq!( - // sqrt(4) = -2 - -a.sqrt().unwrap(), - // 2 - Fp::from_raw_unchecked([ - 0x3213_0000_0006_554f, - 0xb93c_0018_d6c4_0005, - 0x5760_5e0d_b0dd_bb51, - 0x8b25_6521_ed1f_9bcb, - 0x6cf2_8d79_0162_2c03, - 0x11eb_ab9d_bb81_e28c, - ]) - ); -} - -#[test] -fn test_inversion() { - let a = Fp([ - 0x43b4_3a50_78ac_2076, - 0x1ce0_7630_46f8_962b, - 0x724a_5276_486d_735c, - 0x6f05_c2a6_282d_48fd, - 0x2095_bd5b_b4ca_9331, - 0x03b3_5b38_94b0_f7da, - ]); - let b = Fp([ - 0x69ec_d704_0952_148f, - 0x985c_cc20_2219_0f55, - 0xe19b_ba36_a9ad_2f41, - 0x19bb_16c9_5219_dbd8, - 0x14dc_acfd_fb47_8693, - 0x115f_f58a_fff9_a8e1, - ]); - - assert_eq!(a.invert().unwrap(), b); - assert!(bool::from(Fp::zero().invert().is_none())); -} - -#[test] -fn test_lexicographic_largest() { - assert!(!bool::from(Fp::zero().lexicographically_largest())); - assert!(!bool::from(Fp::one().lexicographically_largest())); - assert!(!bool::from( - Fp::from_raw_unchecked([ - 0xa1fa_ffff_fffe_5557, - 0x995b_fff9_76a3_fffe, - 0x03f4_1d24_d174_ceb4, - 0xf654_7998_c199_5dbd, - 0x778a_468f_507a_6034, - 0x0205_5993_1f7f_8103 - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x1804_0000_0001_5554, - 0x8550_0005_3ab0_0001, - 0x633c_b57c_253c_276f, - 0x6e22_d1ec_31eb_b502, - 0xd391_6126_f2d1_4ca2, - 0x17fb_b857_1a00_6596, - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x43f5_ffff_fffc_aaae, - 0x32b7_fff2_ed47_fffd, - 0x07e8_3a49_a2e9_9d69, - 0xeca8_f331_8332_bb7a, - 0xef14_8d1e_a0f4_c069, - 0x040a_b326_3eff_0206, - ]) - .lexicographically_largest() - )); -} diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs deleted file mode 100644 index f6177cc802..0000000000 --- a/bls12_381/src/fp12.rs +++ /dev/null @@ -1,643 +0,0 @@ -use crate::fp::*; -use crate::fp2::*; -use crate::fp6::*; - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$. -pub struct Fp12 { - pub c0: Fp6, - pub c1: Fp6, -} - -impl From for Fp12 { - fn from(f: Fp) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From for Fp12 { - fn from(f: Fp2) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From for Fp12 { - fn from(f: Fp6) -> Fp12 { - Fp12 { - c0: f, - c1: Fp6::zero(), - } - } -} - -impl PartialEq for Fp12 { - fn eq(&self, other: &Fp12) -> bool { - self.ct_eq(other).into() - } -} - -impl Copy for Fp12 {} -impl Clone for Fp12 { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl Default for Fp12 { - fn default() -> Self { - Fp12::zero() - } -} - -impl fmt::Debug for Fp12 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + ({:?})*w", self.c0, self.c1) - } -} - -impl ConditionallySelectable for Fp12 { - #[inline(always)] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp12 { - c0: Fp6::conditional_select(&a.c0, &b.c0, choice), - c1: Fp6::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl ConstantTimeEq for Fp12 { - #[inline(always)] - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Fp12 { - #[inline] - pub fn zero() -> Self { - Fp12 { - c0: Fp6::zero(), - c1: Fp6::zero(), - } - } - - #[inline] - pub fn one() -> Self { - Fp12 { - c0: Fp6::one(), - c1: Fp6::zero(), - } - } - - pub(crate) fn random(rng: &mut R) -> Self { - Fp12 { - c0: Fp6::random(rng), - c1: Fp6::random(rng), - } - } - - pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { - let aa = self.c0.mul_by_01(c0, c1); - let bb = self.c1.mul_by_1(c4); - let o = c1 + c4; - let c1 = self.c1 + self.c0; - let c1 = c1.mul_by_01(c0, &o); - let c1 = c1 - aa - bb; - let c0 = bb; - let c0 = c0.mul_by_nonresidue(); - let c0 = c0 + aa; - - Fp12 { c0, c1 } - } - - #[inline(always)] - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - #[inline(always)] - pub fn conjugate(&self) -> Self { - Fp12 { - c0: self.c0, - c1: -self.c1, - } - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - let c0 = self.c0.frobenius_map(); - let c1 = self.c1.frobenius_map(); - - // c1 = c1 * (u + 1)^((p - 1) / 6) - let c1 = c1 - * Fp6::from(Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0708_9552_b319_d465, - 0xc669_5f92_b50a_8313, - 0x97e8_3ccc_d117_228f, - 0xa35b_aeca_b2dc_29ee, - 0x1ce3_93ea_5daa_ce4d, - 0x08f2_220f_b0fb_66eb, - ]), - c1: Fp::from_raw_unchecked([ - 0xb2f6_6aad_4ce5_d646, - 0x5842_a06b_fc49_7cec, - 0xcf48_95d4_2599_d394, - 0xc11b_9cba_40a8_e8d0, - 0x2e38_13cb_e5a0_de89, - 0x110e_efda_8884_7faf, - ]), - }); - - Fp12 { c0, c1 } - } - - #[inline] - pub fn square(&self) -> Self { - let ab = self.c0 * self.c1; - let c0c1 = self.c0 + self.c1; - let c0 = self.c1.mul_by_nonresidue(); - let c0 = c0 + self.c0; - let c0 = c0 * c0c1; - let c0 = c0 - ab; - let c1 = ab + ab; - let c0 = c0 - ab.mul_by_nonresidue(); - - Fp12 { c0, c1 } - } - - pub fn invert(&self) -> CtOption { - (self.c0.square() - self.c1.square().mul_by_nonresidue()) - .invert() - .map(|t| Fp12 { - c0: self.c0 * t, - c1: self.c1 * -t, - }) - } -} - -impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn mul(self, other: &'b Fp12) -> Self::Output { - let aa = self.c0 * other.c0; - let bb = self.c1 * other.c1; - let o = other.c0 + other.c1; - let c1 = self.c1 + self.c0; - let c1 = c1 * o; - let c1 = c1 - aa; - let c1 = c1 - bb; - let c0 = bb.mul_by_nonresidue(); - let c0 = c0 + aa; - - Fp12 { c0, c1 } - } -} - -impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn add(self, rhs: &'b Fp12) -> Self::Output { - Fp12 { - c0: self.c0 + rhs.c0, - c1: self.c1 + rhs.c1, - } - } -} - -impl<'a> Neg for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn neg(self) -> Self::Output { - Fp12 { - c0: -self.c0, - c1: -self.c1, - } - } -} - -impl Neg for Fp12 { - type Output = Fp12; - - #[inline] - fn neg(self) -> Self::Output { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn sub(self, rhs: &'b Fp12) -> Self::Output { - Fp12 { - c0: self.c0 - rhs.c0, - c1: self.c1 - rhs.c1, - } - } -} - -impl_binops_additive!(Fp12, Fp12); -impl_binops_multiplicative!(Fp12, Fp12); - -#[test] -fn test_arithmetic() { - use crate::fp::*; - use crate::fp2::*; - - let a = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - }; - - let b = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d272_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e348, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd2_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a117_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - }; - - let c = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_71b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0x7791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_133c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_40e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_1744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d3_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_1040, - ]), - }, - }, - }; - - // because a and b and c are similar to each other and - // I was lazy, this is just some arbitrary way to make - // them a little more different - let a = a.square().invert().unwrap().square() + c; - let b = b.square().invert().unwrap().square() + a; - let c = c.square().invert().unwrap().square() + b; - - assert_eq!(a.square(), a * a); - assert_eq!(b.square(), b * b); - assert_eq!(c.square(), c * c); - - assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); - - assert_eq!( - a.invert().unwrap() * b.invert().unwrap(), - (a * b).invert().unwrap() - ); - assert_eq!(a.invert().unwrap() * a, Fp12::one()); - - assert!(a != a.frobenius_map()); - assert_eq!( - a, - a.frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - ); -} diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs deleted file mode 100644 index e9d9275a47..0000000000 --- a/bls12_381/src/fp2.rs +++ /dev/null @@ -1,875 +0,0 @@ -//! This module implements arithmetic over the quadratic extension field Fp2. - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; - -#[derive(Copy, Clone)] -pub struct Fp2 { - pub c0: Fp, - pub c1: Fp, -} - -impl fmt::Debug for Fp2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + {:?}*u", self.c0, self.c1) - } -} - -impl Default for Fp2 { - fn default() -> Self { - Fp2::zero() - } -} - -impl From for Fp2 { - fn from(f: Fp) -> Fp2 { - Fp2 { - c0: f, - c1: Fp::zero(), - } - } -} - -impl ConstantTimeEq for Fp2 { - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Eq for Fp2 {} -impl PartialEq for Fp2 { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Fp2 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp2 { - c0: Fp::conditional_select(&a.c0, &b.c0, choice), - c1: Fp::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl<'a> Neg for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn neg(self) -> Fp2 { - self.neg() - } -} - -impl Neg for Fp2 { - type Output = Fp2; - - #[inline] - fn neg(self) -> Fp2 { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn sub(self, rhs: &'b Fp2) -> Fp2 { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn add(self, rhs: &'b Fp2) -> Fp2 { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn mul(self, rhs: &'b Fp2) -> Fp2 { - self.mul(rhs) - } -} - -impl_binops_additive!(Fp2, Fp2); -impl_binops_multiplicative!(Fp2, Fp2); - -impl Fp2 { - #[inline] - pub const fn zero() -> Fp2 { - Fp2 { - c0: Fp::zero(), - c1: Fp::zero(), - } - } - - #[inline] - pub const fn one() -> Fp2 { - Fp2 { - c0: Fp::one(), - c1: Fp::zero(), - } - } - - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - pub(crate) fn random(rng: &mut R) -> Fp2 { - Fp2 { - c0: Fp::random(rng), - c1: Fp::random(rng), - } - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - // This is always just a conjugation. If you're curious why, here's - // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ - self.conjugate() - } - - #[inline(always)] - pub fn conjugate(&self) -> Self { - Fp2 { - c0: self.c0, - c1: -self.c1, - } - } - - #[inline(always)] - pub fn mul_by_nonresidue(&self) -> Fp2 { - // Multiply a + bu by u + 1, getting - // au + a + bu^2 + bu - // and because u^2 = -1, we get - // (a - b) + (a + b)u - - Fp2 { - c0: self.c0 - self.c1, - c1: self.c0 + self.c1, - } - } - - /// Returns whether or not this element is strictly lexicographically - /// larger than its negation. - #[inline] - pub fn lexicographically_largest(&self) -> Choice { - // If this element's c1 coefficient is lexicographically largest - // then it is lexicographically largest. Otherwise, in the event - // the c1 coefficient is zero and the c0 coefficient is - // lexicographically largest, then this element is lexicographically - // largest. - - self.c1.lexicographically_largest() - | (self.c1.is_zero() & self.c0.lexicographically_largest()) - } - - pub const fn square(&self) -> Fp2 { - // Complex squaring: - // - // v0 = c0 * c1 - // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 - // c1' = 2 * v0 - // - // In BLS12-381's F_{p^2}, our \beta is -1 so we - // can modify this formula: - // - // c0' = (c0 + c1) * (c0 - c1) - // c1' = 2 * c0 * c1 - - let a = (&self.c0).add(&self.c1); - let b = (&self.c0).sub(&self.c1); - let c = (&self.c0).add(&self.c0); - - Fp2 { - c0: (&a).mul(&b), - c1: (&c).mul(&self.c1), - } - } - - pub const fn mul(&self, rhs: &Fp2) -> Fp2 { - // Karatsuba multiplication: - // - // v0 = a0 * b0 - // v1 = a1 * b1 - // c0 = v0 + \beta * v1 - // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 - // - // In BLS12-381's F_{p^2}, our \beta is -1 so we - // can modify this formula. (Also, since we always - // subtract v1, we can compute v1 = -a1 * b1.) - // - // v0 = a0 * b0 - // v1 = (-a1) * b1 - // c0 = v0 + v1 - // c1 = (a0 + a1) * (b0 + b1) - v0 + v1 - - let v0 = (&self.c0).mul(&rhs.c0); - let v1 = (&(&self.c1).neg()).mul(&rhs.c1); - let c0 = (&v0).add(&v1); - let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1)); - let c1 = (&c1).sub(&v0); - let c1 = (&c1).add(&v1); - - Fp2 { c0, c1 } - } - - pub const fn add(&self, rhs: &Fp2) -> Fp2 { - Fp2 { - c0: (&self.c0).add(&rhs.c0), - c1: (&self.c1).add(&rhs.c1), - } - } - - pub const fn sub(&self, rhs: &Fp2) -> Fp2 { - Fp2 { - c0: (&self.c0).sub(&rhs.c0), - c1: (&self.c1).sub(&rhs.c1), - } - } - - pub const fn neg(&self) -> Fp2 { - Fp2 { - c0: (&self.c0).neg(), - c1: (&self.c1).neg(), - } - } - - pub fn sqrt(&self) -> CtOption { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - // with constant time modifications. - - CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { - // a1 = self^((p - 3) / 4) - let a1 = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaaa, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); - - // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) - let alpha = a1.square() * self; - - // x0 = self^((p + 1) / 4) - let x0 = a1 * self; - - // In the event that alpha = -1, the element is order p - 1 and so - // we're just trying to get the square of an element of the subfield - // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element - // x0 = a + bu has b = 0, the solution is therefore au. - CtOption::new( - Fp2 { - c0: -x0.c1, - c1: x0.c0, - }, - alpha.ct_eq(&(&Fp2::one()).neg()), - ) - // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 - .or_else(|| { - CtOption::new( - (alpha + Fp2::one()).pow_vartime(&[ - 0xdcff_7fff_ffff_d555, - 0x0f55_ffff_58a9_ffff, - 0xb398_6950_7b58_7b12, - 0xb23b_a5c2_79c2_895f, - 0x258d_d3db_21a5_d66b, - 0x0d00_88f5_1cbf_f34d, - ]) * x0, - Choice::from(1), - ) - }) - // Only return the result if it's really the square root (and so - // self is actually quadratic nonresidue) - .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) - }) - } - - /// Computes the multiplicative inverse of this field - /// element, returning None in the case that this element - /// is zero. - pub fn invert(&self) -> CtOption { - // We wish to find the multiplicative inverse of a nonzero - // element a + bu in Fp2. We leverage an identity - // - // (a + bu)(a - bu) = a^2 + b^2 - // - // which holds because u^2 = -1. This can be rewritten as - // - // (a + bu)(a - bu)/(a^2 + b^2) = 1 - // - // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). - // This gives that (a - bu)/(a^2 + b^2) is the inverse - // of (a + bu). Importantly, this can be computing using - // only a single inversion in Fp. - - (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 { - c0: self.c0 * t, - c1: self.c1 * -t, - }) - } - - /// Although this is labeled "vartime", it is only - /// variable time with respect to the exponent. It - /// is also not exposed in the public API. - pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res *= self; - } - } - } - res - } -} - -#[test] -fn test_conditional_selection() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), - c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), - }; - - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_equality() { - fn is_equal(a: &Fp2, b: &Fp2) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); - - assert_eq!(eq, bool::from(ct_eq)); - - eq - } - - assert!(is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); -} - -#[test] -fn test_squaring() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - - assert_eq!(a.square(), b); -} - -#[test] -fn test_multiplication() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf597_483e_27b4_e0f7, - 0x610f_badf_811d_ae5f, - 0x8432_af91_7714_327a, - 0x6a9a_9603_cf88_f09e, - 0xf05a_7bf8_bad0_eb01, - 0x0954_9131_c003_ffae, - ]), - c1: Fp::from_raw_unchecked([ - 0x963b_02d0_f93d_37cd, - 0xc95c_e1cd_b30a_73d4, - 0x3087_25fa_3126_f9b8, - 0x56da_3c16_7fab_0d50, - 0x6b50_86b5_f4b6_d6af, - 0x09c3_9f06_2f18_e9f2, - ]), - }; - - assert_eq!(a * b, c); -} - -#[test] -fn test_addition() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6b82_a9a7_08c1_32d2, - 0x476b_1da3_39ba_5ba4, - 0x848c_0e62_4b91_cd87, - 0x11f9_5955_295a_99ec, - 0xf337_6fce_2255_9f06, - 0x0c3f_e3fa_ce8c_8f43, - ]), - c1: Fp::from_raw_unchecked([ - 0x6f99_2c12_73ab_5bc5, - 0x3355_1366_17a1_df33, - 0x8b0e_f74c_0aed_aff9, - 0x062f_9246_8ad2_ca12, - 0xe146_9770_738f_d584, - 0x12c3_c3dd_84bc_a26d, - ]), - }; - - assert_eq!(a + b, c); -} - -#[test] -fn test_subtraction() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe1c0_86bb_bf1b_5981, - 0x4faf_c3a9_aa70_5d7e, - 0x2734_b5c1_0bb7_e726, - 0xb2bd_7776_af03_7a3e, - 0x1b89_5fb3_98a8_4164, - 0x1730_4aef_6f11_3cec, - ]), - c1: Fp::from_raw_unchecked([ - 0x74c3_1c79_9519_1204, - 0x3271_aa54_79fd_ad2b, - 0xc9b4_7157_4915_a30f, - 0x65e4_0313_ec44_b8be, - 0x7487_b238_5b70_67cb, - 0x0952_3b26_d0ad_19a4, - ]), - }; - - assert_eq!(a - b, c); -} - -#[test] -fn test_negation() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf05c_e7ce_9c11_39d7, - 0x6274_8f57_97e8_a36d, - 0xc4e8_d9df_c664_96df, - 0xb457_88e1_8118_9209, - 0x6949_13d0_8772_930d, - 0x1549_836a_3770_f3cf, - ]), - c1: Fp::from_raw_unchecked([ - 0x24d0_5bb9_fb9d_491c, - 0xfb1e_a120_c12e_39d0, - 0x7067_879f_c807_c7b1, - 0x60a9_269a_31bb_dab6, - 0x45c2_56bc_fd71_649b, - 0x18f6_9b5d_2b8a_fbde, - ]), - }; - - assert_eq!(-a, b); -} - -#[test] -fn test_sqrt() { - // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x2bee_d146_27d7_f9e9, - 0xb661_4e06_660e_5dce, - 0x06c4_cc7c_2f91_d42c, - 0x996d_7847_4b7a_63cc, - 0xebae_bc4c_820d_574e, - 0x1886_5e12_d93f_d845, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d82_8664_baf4_f566, - 0xd17e_6639_96ec_7339, - 0x679e_ad55_cb40_78d0, - 0xfe3b_2260_e001_ec28, - 0x3059_93d0_43d9_1b68, - 0x0626_f03c_0489_b72d, - ]), - }; - - assert_eq!(a.sqrt().unwrap().square(), a); - - // b = 5, which is a generator of the p - 1 order - // multiplicative subgroup - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6631_0000_0010_5545, - 0x2114_0040_0eec_000d, - 0x3fa7_af30_c820_e316, - 0xc52a_8b8d_6387_695d, - 0x9fb4_e61d_1e83_eac5, - 0x005c_b922_afe8_4dc7, - ]), - c1: Fp::zero(), - }; - - assert_eq!(b.sqrt().unwrap().square(), b); - - // c = 25, which is a generator of the (p - 1) / 2 order - // multiplicative subgroup - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x44f6_0000_0051_ffae, - 0x86b8_0141_9948_0043, - 0xd715_9952_f1f3_794a, - 0x755d_6e3d_fe1f_fc12, - 0xd36c_d6db_5547_e905, - 0x02f8_c8ec_bf18_67bb, - ]), - c1: Fp::zero(), - }; - - assert_eq!(c.sqrt().unwrap().square(), c); - - // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 - // is nonsquare. - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc5fa_1bc8_fd00_d7f6, - 0x3830_ca45_4606_003b, - 0x2b28_7f11_04b1_02da, - 0xa7fb_30f2_8230_f23e, - 0x339c_db9e_e953_dbf0, - 0x0d78_ec51_d989_fc57, - ]), - c1: Fp::from_raw_unchecked([ - 0x27ec_4898_cf87_f613, - 0x9de1_394e_1abb_05a5, - 0x0947_f85d_c170_fc14, - 0x586f_bc69_6b61_14b7, - 0x2b34_75a4_077d_7169, - 0x13e1_c895_cc4b_6c22, - ]) - } - .sqrt() - .is_none() - )); -} - -#[test] -fn test_inversion() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - }; - - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0581_a133_3d4f_48a6, - 0x5824_2f6e_f074_8500, - 0x0292_c955_349e_6da5, - 0xba37_721d_dd95_fcd0, - 0x70d1_6790_3aa5_dfc5, - 0x1189_5e11_8b58_a9d5, - ]), - c1: Fp::from_raw_unchecked([ - 0x0eda_09d2_d7a8_5d17, - 0x8808_e137_a7d1_a2cf, - 0x43ae_2625_c1ff_21db, - 0xf85a_c9fd_f7a7_4c64, - 0x8fcc_dda5_b8da_9738, - 0x08e8_4f0c_b32c_d17d, - ]), - }; - - assert_eq!(a.invert().unwrap(), b); - - assert!(bool::from(Fp2::zero().invert().is_none())); -} - -#[test] -fn test_lexicographic_largest() { - assert!(!bool::from(Fp2::zero().lexicographically_largest())); - assert!(!bool::from(Fp2::one().lexicographically_largest())); - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: -Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::zero(), - } - .lexicographically_largest() - )); - assert!(bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::zero(), - } - .lexicographically_largest() - )); -} diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs deleted file mode 100644 index 9df150f657..0000000000 --- a/bls12_381/src/fp6.rs +++ /dev/null @@ -1,513 +0,0 @@ -use crate::fp::*; -use crate::fp2::*; - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. -pub struct Fp6 { - pub c0: Fp2, - pub c1: Fp2, - pub c2: Fp2, -} - -impl From for Fp6 { - fn from(f: Fp) -> Fp6 { - Fp6 { - c0: Fp2::from(f), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } -} - -impl From for Fp6 { - fn from(f: Fp2) -> Fp6 { - Fp6 { - c0: f, - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } -} - -impl PartialEq for Fp6 { - fn eq(&self, other: &Fp6) -> bool { - self.ct_eq(other).into() - } -} - -impl Copy for Fp6 {} -impl Clone for Fp6 { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl Default for Fp6 { - fn default() -> Self { - Fp6::zero() - } -} - -impl fmt::Debug for Fp6 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2) - } -} - -impl ConditionallySelectable for Fp6 { - #[inline(always)] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp6 { - c0: Fp2::conditional_select(&a.c0, &b.c0, choice), - c1: Fp2::conditional_select(&a.c1, &b.c1, choice), - c2: Fp2::conditional_select(&a.c2, &b.c2, choice), - } - } -} - -impl ConstantTimeEq for Fp6 { - #[inline(always)] - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) - } -} - -impl Fp6 { - #[inline] - pub fn zero() -> Self { - Fp6 { - c0: Fp2::zero(), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } - - #[inline] - pub fn one() -> Self { - Fp6 { - c0: Fp2::one(), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } - - pub(crate) fn random(rng: &mut R) -> Self { - Fp6 { - c0: Fp2::random(rng), - c1: Fp2::random(rng), - c2: Fp2::random(rng), - } - } - - pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { - let b_b = self.c1 * c1; - - let t1 = (self.c1 + self.c2) * c1 - b_b; - let t1 = t1.mul_by_nonresidue(); - - let t2 = (self.c0 + self.c1) * c1 - b_b; - - Fp6 { - c0: t1, - c1: t2, - c2: b_b, - } - } - - pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 { - let a_a = self.c0 * c0; - let b_b = self.c1 * c1; - - let t1 = (self.c1 + self.c2) * c1 - b_b; - let t1 = t1.mul_by_nonresidue() + a_a; - - let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; - - let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; - - Fp6 { - c0: t1, - c1: t2, - c2: t3, - } - } - - /// Multiply by quadratic nonresidue v. - pub fn mul_by_nonresidue(&self) -> Self { - // Given a + bv + cv^2, this produces - // av + bv^2 + cv^3 - // but because v^3 = u + 1, we have - // c(u + 1) + av + v^2 - - Fp6 { - c0: self.c2.mul_by_nonresidue(), - c1: self.c0, - c2: self.c1, - } - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - let c0 = self.c0.frobenius_map(); - let c1 = self.c1.frobenius_map(); - let c2 = self.c2.frobenius_map(); - - // c1 = c1 * (u + 1)^((p - 1) / 3) - let c1 = c1 - * Fp2 { - c0: Fp::zero(), - c1: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - }; - - // c2 = c2 * (u + 1)^((2p - 2) / 3) - let c2 = c2 - * Fp2 { - c0: Fp::from_raw_unchecked([ - 0x890d_c9e4_8675_45c3, - 0x2af3_2253_3285_a5d5, - 0x5088_0866_309b_7e2c, - 0xa20d_1b8c_7e88_1024, - 0x14e4_f04f_e2db_9068, - 0x14e5_6d3f_1564_853a, - ]), - c1: Fp::zero(), - }; - - Fp6 { c0, c1, c2 } - } - - #[inline(always)] - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero() - } - - #[inline] - pub fn square(&self) -> Self { - let s0 = self.c0.square(); - let ab = self.c0 * self.c1; - let s1 = ab + ab; - let s2 = (self.c0 - self.c1 + self.c2).square(); - let bc = self.c1 * self.c2; - let s3 = bc + bc; - let s4 = self.c2.square(); - - Fp6 { - c0: s3.mul_by_nonresidue() + s0, - c1: s4.mul_by_nonresidue() + s1, - c2: s1 + s2 + s3 - s0 - s4, - } - } - - #[inline] - pub fn invert(&self) -> CtOption { - let c0 = (self.c1 * self.c2).mul_by_nonresidue(); - let c0 = self.c0.square() - c0; - - let c1 = self.c2.square().mul_by_nonresidue(); - let c1 = c1 - (self.c0 * self.c1); - - let c2 = self.c1.square(); - let c2 = c2 - (self.c0 * self.c2); - - let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue(); - let tmp = tmp + (self.c0 * c0); - - tmp.invert().map(|t| Fp6 { - c0: t * c0, - c1: t * c1, - c2: t * c2, - }) - } -} - -impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn mul(self, other: &'b Fp6) -> Self::Output { - let aa = self.c0 * other.c0; - let bb = self.c1 * other.c1; - let cc = self.c2 * other.c2; - - let t1 = other.c1 + other.c2; - let tmp = self.c1 + self.c2; - let t1 = t1 * tmp; - let t1 = t1 - bb; - let t1 = t1 - cc; - let t1 = t1.mul_by_nonresidue(); - let t1 = t1 + aa; - - let t3 = other.c0 + other.c2; - let tmp = self.c0 + self.c2; - let t3 = t3 * tmp; - let t3 = t3 - aa; - let t3 = t3 + bb; - let t3 = t3 - cc; - - let t2 = other.c0 + other.c1; - let tmp = self.c0 + self.c1; - let t2 = t2 * tmp; - let t2 = t2 - aa; - let t2 = t2 - bb; - let cc = cc.mul_by_nonresidue(); - let t2 = t2 + cc; - - Fp6 { - c0: t1, - c1: t2, - c2: t3, - } - } -} - -impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn add(self, rhs: &'b Fp6) -> Self::Output { - Fp6 { - c0: self.c0 + rhs.c0, - c1: self.c1 + rhs.c1, - c2: self.c2 + rhs.c2, - } - } -} - -impl<'a> Neg for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn neg(self) -> Self::Output { - Fp6 { - c0: -self.c0, - c1: -self.c1, - c2: -self.c2, - } - } -} - -impl Neg for Fp6 { - type Output = Fp6; - - #[inline] - fn neg(self) -> Self::Output { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn sub(self, rhs: &'b Fp6) -> Self::Output { - Fp6 { - c0: self.c0 - rhs.c0, - c1: self.c1 - rhs.c1, - c2: self.c2 - rhs.c2, - } - } -} - -impl_binops_additive!(Fp6, Fp6); -impl_binops_multiplicative!(Fp6, Fp6); - -#[test] -fn test_arithmetic() { - use crate::fp::*; - - let a = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }; - - let b = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf120_cb98_b16f_d84b, - 0x5fb5_10cf_f3de_1d61, - 0x0f21_a5d0_69d8_c251, - 0xaa1f_d62f_34f2_839a, - 0x5a13_3515_7f89_913f, - 0x14a3_fe32_9643_c247, - ]), - c1: Fp::from_raw_unchecked([ - 0x3516_cb98_b16c_82f9, - 0x926d_10c2_e126_1d5f, - 0x1709_e01a_0cc2_5fba, - 0x96c8_c960_b825_3f14, - 0x4927_c234_207e_51a9, - 0x18ae_b158_d542_c44e, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xbf0d_cb98_b169_82fc, - 0xa679_10b7_1d1a_1d5c, - 0xb7c1_47c2_b8fb_06ff, - 0x1efa_710d_47d2_e7ce, - 0xed20_a79c_7e27_653c, - 0x02b8_5294_dac1_dfba, - ]), - c1: Fp::from_raw_unchecked([ - 0x9d52_cb98_b180_82e5, - 0x621d_1111_5176_1d6f, - 0xe798_8260_3b48_af43, - 0x0ad3_1637_a4f4_da37, - 0xaeac_737c_5ac1_cf2e, - 0x006e_7e73_5b48_b824, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe148_cb98_b17d_2d93, - 0x94d5_1104_3ebe_1d6c, - 0xef80_bca9_de32_4cac, - 0xf77c_0969_2827_95b1, - 0x9dc1_009a_fbb6_8f97, - 0x0479_3199_9a47_ba2b, - ]), - c1: Fp::from_raw_unchecked([ - 0x253e_cb98_b179_d841, - 0xc78d_10f7_2c06_1d6a, - 0xf768_f6f3_811b_ea15, - 0xe424_fc9a_ab5a_512b, - 0x8cd5_8db9_9cab_5001, - 0x0883_e4bf_d946_bc32, - ]), - }, - }; - - let c = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6934_cb98_b176_82ef, - 0xfa45_10ea_194e_1d67, - 0xff51_313d_2405_877e, - 0xd0cd_efcc_2e8d_0ca5, - 0x7bea_1ad8_3da0_106b, - 0x0c8e_97e6_1845_be39, - ]), - c1: Fp::from_raw_unchecked([ - 0x4779_cb98_b18d_82d8, - 0xb5e9_1144_4daa_1d7a, - 0x2f28_6bda_a653_2fc2, - 0xbca6_94f6_8bae_ff0f, - 0x3d75_e6b8_1a3a_7a5d, - 0x0a44_c3c4_98cc_96a3, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8b6f_cb98_b18a_2d86, - 0xe8a1_1137_3af2_1d77, - 0x3710_a624_493c_cd2b, - 0xa94f_8828_0ee1_ba89, - 0x2c8a_73d6_bb2f_3ac7, - 0x0e4f_76ea_d7cb_98aa, - ]), - c1: Fp::from_raw_unchecked([ - 0xcf65_cb98_b186_d834, - 0x1b59_112a_283a_1d74, - 0x3ef8_e06d_ec26_6a95, - 0x95f8_7b59_9214_7603, - 0x1b9f_00f5_5c23_fb31, - 0x125a_2a11_16ca_9ab1, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x135b_cb98_b183_82e2, - 0x4e11_111d_1582_1d72, - 0x46e1_1ab7_8f10_07fe, - 0x82a1_6e8b_1547_317d, - 0x0ab3_8e13_fd18_bb9b, - 0x1664_dd37_55c9_9cb8, - ]), - c1: Fp::from_raw_unchecked([ - 0xce65_cb98_b131_8334, - 0xc759_0fdb_7c3a_1d2e, - 0x6fcb_8164_9d1c_8eb3, - 0x0d44_004d_1727_356a, - 0x3746_b738_a7d0_d296, - 0x136c_144a_96b1_34fc, - ]), - }, - }; - - assert_eq!(a.square(), a * a); - assert_eq!(b.square(), b * b); - assert_eq!(c.square(), c * c); - - assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); - - assert_eq!( - a.invert().unwrap() * b.invert().unwrap(), - (a * b).invert().unwrap() - ); - assert_eq!(a.invert().unwrap() * a, Fp6::one()); -} diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs deleted file mode 100644 index d30f882401..0000000000 --- a/bls12_381/src/g1.rs +++ /dev/null @@ -1,1678 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G1Affine { - pub(crate) x: Fp, - pub(crate) y: Fp, - infinity: Choice, -} - -impl Default for G1Affine { - fn default() -> G1Affine { - G1Affine::identity() - } -} - -impl fmt::Display for G1Affine { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G1Projective> for G1Affine { - fn from(p: &'a G1Projective) -> G1Affine { - let zinv = p.z.invert().unwrap_or(Fp::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G1Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) - } -} - -impl From for G1Affine { - fn from(p: G1Projective) -> G1Affine { - G1Affine::from(&p) - } -} - -impl ConstantTimeEq for G1Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G1Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Affine { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G1Affine {} -impl PartialEq for G1Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - G1Affine { - x: self.x, - y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Affine) -> G1Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Affine) -> G1Projective { - self + (-rhs) - } -} - -impl Sum for G1Projective -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl_binops_additive!(G1Projective, G1Affine); -impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); - -const B: Fp = Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, -]); - -impl G1Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Affine { - G1Affine { - x: Fp::zero(), - y: Fp::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Affine { - G1Affine { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 48] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes(); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 96] { - let mut res = [0; 96]; - - res[0..48].copy_from_slice( - &Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..], - ); - res[48..96].copy_from_slice( - &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..], - ); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let y = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - y.and_then(|y| { - // Create a point representing this value - let p = G1Affine::conditional_select( - &G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G1Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 48]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G1Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G1Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4 - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G1Projective { - x: Fp, - y: Fp, - z: Fp, -} - -impl Default for G1Projective { - fn default() -> G1Projective { - G1Projective::identity() - } -} - -impl fmt::Display for G1Projective { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G1Affine> for G1Projective { - fn from(p: &'a G1Affine) -> G1Projective { - G1Projective { - x: p.x, - y: p.y, - z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), - } - } -} - -impl From for G1Projective { - fn from(p: G1Affine) -> G1Projective { - G1Projective::from(&p) - } -} - -impl ConstantTimeEq for G1Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G1Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Projective { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - z: Fp::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G1Projective {} -impl PartialEq for G1Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - G1Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G1Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G1Projective, G1Projective); -impl_binops_multiplicative!(G1Projective, Scalar); -impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); - -impl G1Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Projective { - G1Projective { - x: Fp::zero(), - y: Fp::one(), - z: Fp::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Projective { - G1Projective { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - z: Fp::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G1Projective { - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G1Projective) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8; 32]) -> G1Projective { - let mut acc = G1Projective::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G1Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - /// Multiply `self` by `crate::BLS_X`, using double and add. - fn mul_by_x(&self) -> G1Projective { - let mut xself = G1Projective::identity(); - // NOTE: in BLS12-381 we can just skip the first bit. - let mut x = crate::BLS_X >> 1; - let mut tmp = *self; - while x != 0 { - tmp = tmp.double(); - - if x % 2 == 1 { - xself += tmp; - } - x >>= 1; - } - // finally, flip the sign - if crate::BLS_X_IS_NEGATIVE { - xself = -xself; - } - xself - } - - /// Multiplies by $(1 - z)$, where $z$ is the parameter of BLS12-381, which - /// [suffices to clear](https://ia.cr/2019/403) the cofactor and map - /// elliptic curve points to elements of $\mathbb{G}\_1$. - pub fn clear_cofactor(&self) -> G1Projective { - self - self.mul_by_x() - } - - /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G1Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -pub struct G1Compressed([u8; 48]); - -impl fmt::Debug for G1Compressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G1Compressed { - fn default() -> Self { - G1Compressed([0; 48]) - } -} - -impl AsRef<[u8]> for G1Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G1Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -pub struct G1Uncompressed([u8; 96]); - -impl fmt::Debug for G1Uncompressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G1Uncompressed { - fn default() -> Self { - G1Uncompressed([0; 96]) - } -} - -impl AsRef<[u8]> for G1Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G1Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Group for G1Projective { - type Scalar = Scalar; - - fn random(rng: &mut R) -> Self { - loop { - let x = Fp::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) - let p = ((x.square() * x) + B).sqrt().map(|y| G1Affine { - x, - y: if flip_sign { -y } else { y }, - infinity: 0.into(), - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve().clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl WnafGroup for G1Projective { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for G1Projective {} - -impl Curve for G1Projective { - type AffineRepr = G1Affine; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl PrimeCurve for G1Projective { - type Affine = G1Affine; -} - -impl PrimeCurveAffine for G1Affine { - type Scalar = Scalar; - type Curve = G1Projective; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - self.into() - } -} - -impl GroupEncoding for G1Projective { - type Repr = G1Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - G1Affine::from_bytes(bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - G1Affine::from_bytes_unchecked(bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - G1Affine::from(self).to_bytes() - } -} - -impl GroupEncoding for G1Affine { - type Repr = G1Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_compressed(&bytes.0) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_compressed_unchecked(&bytes.0) - } - - fn to_bytes(&self) -> Self::Repr { - G1Compressed(self.to_compressed()) - } -} - -impl UncompressedEncoding for G1Affine { - type Uncompressed = G1Uncompressed; - - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed(&bytes.0) - } - - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed_unchecked(&bytes.0) - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - G1Uncompressed(self.to_uncompressed()) - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G1Affine::identity().is_on_curve())); - assert!(bool::from(G1Affine::generator().is_on_curve())); - assert!(bool::from(G1Projective::identity().is_on_curve())); - assert!(bool::from(G1Projective::generator().is_on_curve())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let gen = G1Affine::generator(); - let mut test = G1Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let mut c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(bool::from(G1Affine::from(a).is_on_curve())); - assert!(!bool::from(G1Affine::from(a).is_identity())); - assert!(bool::from(G1Affine::from(b).is_on_curve())); - assert!(bool::from(G1Affine::from(b).is_identity())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G1Affine::from(c), G1Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(bool::from(G1Projective::from(a).is_on_curve())); - assert!(!bool::from(G1Projective::from(a).is_identity())); - assert!(bool::from(G1Projective::from(b).is_on_curve())); - assert!(bool::from(G1Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G1Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G1Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G1Affine::from(tmp), - G1Affine { - x: Fp::from_raw_unchecked([ - 0x53e9_78ce_58a9_ba3c, - 0x3ea0_583c_4f3d_65f9, - 0x4d20_bb47_f001_2960, - 0xa54c_664a_e5b2_b5d9, - 0x26b5_52a3_9d7e_b21f, - 0x0008_895d_26e6_8785, - ]), - y: Fp::from_raw_unchecked([ - 0x7011_0b32_9829_3940, - 0xda33_c539_3f1f_6afc, - 0xb86e_dfd1_6a5a_a785, - 0xaec6_d1c9_e7b1_c895, - 0x25cf_c2b5_22d1_1720, - 0x0636_1c83_f8d0_9b15, - ]), - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G1Projective::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G1Affine::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G1Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G1Projective::generator().double(); - assert_eq!(a + (-a), G1Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G1Affine::generator(); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G1Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G1Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G1Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G1Affine { - x: Fp::from_raw_unchecked([ - 0x0aba_f895_b97e_43c8, - 0xba4c_6432_eb9b_61b0, - 0x1250_6f52_adfe_307f, - 0x7502_8c34_3933_6b72, - 0x8474_4f05_b8e9_bd71, - 0x113d_554f_b095_54f7, - ]), - y: Fp::from_raw_unchecked([ - 0x73e9_0e88_f5cf_01c0, - 0x3700_7b65_dd31_97e2, - 0x5cf9_a199_2f0d_7c78, - 0x4f83_c10b_9eb3_330d, - 0xf6a6_3f6f_07f6_0961, - 0x0c53_b5b9_7e63_4df3, - ]), - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G1Affine::identity().is_torsion_free())); - assert!(bool::from(G1Affine::generator().is_torsion_free())); -} - -#[test] -fn test_mul_by_x() { - // multiplying by `x` a point in G1 is the same as multiplying by - // the equivalent scalar. - let generator = G1Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -Scalar::from(crate::BLS_X) - } else { - Scalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); - - let point = G1Projective::generator() * Scalar::from(42); - assert_eq!(point.mul_by_x(), point * x); -} - -#[test] -fn test_clear_cofactor() { - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G1Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G1Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - let point = G1Projective { - x: Fp::from_raw_unchecked([ - 0x48af5ff540c817f0, - 0xd73893acaf379d5a, - 0xe6c43584e18e023c, - 0x1eda39c30f188b3e, - 0xf618c6d3ccc0f8d8, - 0x0073542cd671e16c, - ]), - y: Fp::from_raw_unchecked([ - 0x57bf8be79461d0ba, - 0xfc61459cee3547c3, - 0x0d23567df1ef147b, - 0x0ee187bcce1d9b64, - 0xb0c8cfbe9dc8fdc1, - 0x1328661767ef368b, - ]), - z: Fp::from_raw_unchecked([ - 0x3d2d1c670671394e, - 0x0ee3a800a2f7c1ca, - 0x270f4f21da2e5050, - 0xe02840a53f1be768, - 0x55debeb597512690, - 0x08bd25353dc8f791, - ]), - }; - - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G1Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free())); - - // in BLS12-381 the cofactor in G1 can be - // cleared multiplying by (1-x) - let h_eff = Scalar::from(1) + Scalar::from(crate::BLS_X); - assert_eq!(point.clear_cofactor(), point * h_eff); -} - -#[test] -fn test_batch_normalize() { - let a = G1Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G1Projective::identity() - } - if b_identity { - v[1] = G1Projective::identity() - } - if c_identity { - v[2] = G1Projective::identity() - } - - let mut t = [ - G1Affine::identity(), - G1Affine::identity(), - G1Affine::identity(), - ]; - let expected = [ - G1Affine::from(v[0]), - G1Affine::from(v[1]), - G1Affine::from(v[2]), - ]; - - G1Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs deleted file mode 100644 index b0e7a87638..0000000000 --- a/bls12_381/src/g2.rs +++ /dev/null @@ -1,2142 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::fp2::Fp2; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G2Affine { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - infinity: Choice, -} - -impl Default for G2Affine { - fn default() -> G2Affine { - G2Affine::identity() - } -} - -impl fmt::Display for G2Affine { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G2Projective> for G2Affine { - fn from(p: &'a G2Projective) -> G2Affine { - let zinv = p.z.invert().unwrap_or(Fp2::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G2Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) - } -} - -impl From for G2Affine { - fn from(p: G2Projective) -> G2Affine { - G2Affine::from(&p) - } -} - -impl ConstantTimeEq for G2Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G2Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Affine { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G2Affine {} -impl PartialEq for G2Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - G2Affine { - x: self.x, - y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Affine) -> G2Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Affine) -> G2Projective { - self + (-rhs) - } -} - -impl Sum for G2Projective -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl_binops_additive!(G2Projective, G2Affine); -impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); - -const B: Fp2 = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]), - c1: Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]), -}; - -impl G2Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Affine { - G2Affine { - x: Fp2::zero(), - y: Fp2::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Affine { - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 96] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - - let mut res = [0; 96]; - - (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); - (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 192] { - let mut res = [0; 192]; - - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); - - res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); - res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); - res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); - res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let yc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[96..144]); - - Fp::from_bytes(&tmp) - }; - let yc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[144..192]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - yc1.and_then(|yc1| { - yc0.and_then(|yc0| { - let x = Fp2 { - c0: xc0, - c1: xc1 - }; - let y = Fp2 { - c0: yc0, - c1: yc1 - }; - - // Create a point representing this value - let p = G2Affine::conditional_select( - &G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G2Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 96]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - let x = Fp2 { c0: xc0, c1: xc1 }; - - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G2Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp2::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G2Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4(u + 1) - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G2Projective { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - pub(crate) z: Fp2, -} - -impl Default for G2Projective { - fn default() -> G2Projective { - G2Projective::identity() - } -} - -impl fmt::Display for G2Projective { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G2Affine> for G2Projective { - fn from(p: &'a G2Affine) -> G2Projective { - G2Projective { - x: p.x, - y: p.y, - z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), - } - } -} - -impl From for G2Projective { - fn from(p: G2Affine) -> G2Projective { - G2Projective::from(&p) - } -} - -impl ConstantTimeEq for G2Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G2Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Projective { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - z: Fp2::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G2Projective {} -impl PartialEq for G2Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - G2Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G2Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G2Projective, G2Projective); -impl_binops_multiplicative!(G2Projective, Scalar); -impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); - -impl G2Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Projective { - G2Projective { - x: Fp2::zero(), - y: Fp2::one(), - z: Fp2::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Projective { - G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - z: Fp2::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G2Projective { - // http://www.hyperelliptic.org/EFD/g2p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G2Projective) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8]) -> G2Projective { - let mut acc = G2Projective::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G2Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - #[cfg(feature = "endo")] - fn psi(&self) -> G2Projective { - // 1 / ((u+1) ^ ((q-1)/3)) - let psi_coeff_x = Fp2 { - c0: Fp::zero(), - c1: Fp::from_raw_unchecked([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ]), - }; - // 1 / ((u+1) ^ (p-1)/2) - let psi_coeff_y = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0x0bd592fc7d825ec8, - ]), - c1: Fp::from_raw_unchecked([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0x0e2b7eedbbfd87d2, - ]), - }; - - G2Projective { - // x = frobenius(x)/((u+1)^((p-1)/3)) - x: self.x.frobenius_map() * psi_coeff_x, - // y = frobenius(y)/(u+1)^((p-1)/2) - y: self.y.frobenius_map() * psi_coeff_y, - // z = frobenius(z) - z: self.z.frobenius_map(), - } - } - - #[cfg(feature = "endo")] - fn psi2(&self) -> G2Projective { - // 1 / 2 ^ ((q-1)/3) - let psi2_coeff_x = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x03f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fp::zero(), - }; - - G2Projective { - // x = frobenius^2(x)/2^((p-1)/3) - x: self.x.frobenius_map().frobenius_map() * psi2_coeff_x, - // y = -frobenius^2(y) - y: self.y.frobenius_map().frobenius_map().neg(), - // z = z - z: self.z, - } - } - - /// Multiply `self` by `crate::BLS_X`, using double and add. - #[cfg(feature = "endo")] - fn mul_by_x(&self) -> G2Projective { - let mut xself = G2Projective::identity(); - // NOTE: in BLS12-381 we can just skip the first bit. - let mut x = crate::BLS_X >> 1; - let mut acc = *self; - while x != 0 { - acc = acc.double(); - if x % 2 == 1 { - xself += acc; - } - x >>= 1; - } - // finally, flip the sign - if crate::BLS_X_IS_NEGATIVE { - xself = -xself; - } - xself - } - - /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). - /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot - /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the - /// parameter of BLS12-381. - /// - /// The endomorphism is only actually used if the crate feature `endo` is - /// enabled, and it is disabled by default to mitigate potential patent - /// issues. - pub fn clear_cofactor(&self) -> G2Projective { - #[cfg(feature = "endo")] - fn clear_cofactor(this: &G2Projective) -> G2Projective { - let t1 = this.mul_by_x(); // [x] P - let t2 = this.psi(); // psi(P) - - this.double().psi2() // psi^2(2P) - + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) - - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) - - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) - - this // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) - } - - #[cfg(not(feature = "endo"))] - fn clear_cofactor(this: &G2Projective) -> G2Projective { - this.multiply(&[ - 0x51, 0x55, 0xa9, 0xaa, 0x5, 0x0, 0x2, 0xe8, 0xb4, 0xf6, 0xbb, 0xde, 0xa, 0x4c, - 0x89, 0x59, 0xa3, 0xf6, 0x89, 0x66, 0xc0, 0xcb, 0x54, 0xe9, 0x1a, 0x7c, 0x47, 0xd7, - 0x69, 0xec, 0xc0, 0x2e, 0xb0, 0x12, 0x12, 0x5d, 0x1, 0xbf, 0x82, 0x6d, 0x95, 0xdb, - 0x31, 0x87, 0x17, 0x2f, 0x9c, 0x32, 0xe1, 0xff, 0x8, 0x15, 0x3, 0xff, 0x86, 0x99, - 0x68, 0xd7, 0x5a, 0x14, 0xe9, 0xa8, 0xe2, 0x88, 0x28, 0x35, 0x1b, 0xa9, 0xe, 0x6a, - 0x4c, 0x58, 0xb3, 0x75, 0xee, 0xf2, 0x8, 0x9f, 0xc6, 0xb, - ]) - } - - clear_cofactor(self) - } - - /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp2::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G2Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(u + 1)(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -pub struct G2Compressed([u8; 96]); - -impl fmt::Debug for G2Compressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G2Compressed { - fn default() -> Self { - G2Compressed([0; 96]) - } -} - -impl AsRef<[u8]> for G2Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G2Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -pub struct G2Uncompressed([u8; 192]); - -impl fmt::Debug for G2Uncompressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G2Uncompressed { - fn default() -> Self { - G2Uncompressed([0; 192]) - } -} - -impl AsRef<[u8]> for G2Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G2Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Group for G2Projective { - type Scalar = Scalar; - - fn random(rng: &mut R) -> Self { - loop { - let x = Fp2::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) - let p = ((x.square() * x) + B).sqrt().map(|y| G2Affine { - x, - y: if flip_sign { -y } else { y }, - infinity: 0.into(), - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve().clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl WnafGroup for G2Projective { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for G2Projective {} - -impl Curve for G2Projective { - type AffineRepr = G2Affine; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl PrimeCurve for G2Projective { - type Affine = G2Affine; -} - -impl PrimeCurveAffine for G2Affine { - type Scalar = Scalar; - type Curve = G2Projective; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - self.into() - } -} - -impl GroupEncoding for G2Projective { - type Repr = G2Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - G2Affine::from_bytes(bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - G2Affine::from_bytes_unchecked(bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - G2Affine::from(self).to_bytes() - } -} - -impl GroupEncoding for G2Affine { - type Repr = G2Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_compressed(&bytes.0) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_compressed_unchecked(&bytes.0) - } - - fn to_bytes(&self) -> Self::Repr { - G2Compressed(self.to_compressed()) - } -} - -impl UncompressedEncoding for G2Affine { - type Uncompressed = G2Uncompressed; - - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed(&bytes.0) - } - - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed_unchecked(&bytes.0) - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - G2Uncompressed(self.to_uncompressed()) - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G2Affine::identity().is_on_curve())); - assert!(bool::from(G2Affine::generator().is_on_curve())); - assert!(bool::from(G2Projective::identity().is_on_curve())); - assert!(bool::from(G2Projective::generator().is_on_curve())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let gen = G2Affine::generator(); - let mut test = G2Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let mut c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(bool::from(G2Affine::from(a).is_on_curve())); - assert!(!bool::from(G2Affine::from(a).is_identity())); - assert!(bool::from(G2Affine::from(b).is_on_curve())); - assert!(bool::from(G2Affine::from(b).is_identity())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G2Affine::from(c), G2Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(bool::from(G2Projective::from(a).is_on_curve())); - assert!(!bool::from(G2Projective::from(a).is_identity())); - assert!(bool::from(G2Projective::from(b).is_on_curve())); - assert!(bool::from(G2Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G2Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G2Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G2Affine::from(tmp), - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe9d9_e2da_9620_f98b, - 0x54f1_1993_46b9_7f36, - 0x3db3_b820_376b_ed27, - 0xcfdb_31c9_b0b6_4f4c, - 0x41d7_c127_8635_4493, - 0x0571_0794_c255_c064, - ]), - c1: Fp::from_raw_unchecked([ - 0xd6c1_d3ca_6ea0_d06e, - 0xda0c_bd90_5595_489f, - 0x4f53_52d4_3479_221d, - 0x8ade_5d73_6f8c_97e0, - 0x48cc_8433_925e_f70e, - 0x08d7_ea71_ea91_ef81, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x15ba_26eb_4b0d_186f, - 0x0d08_6d64_b7e9_e01e, - 0xc8b8_48dd_652f_4c78, - 0xeecf_46a6_123b_ae4f, - 0x255e_8dd8_b6dc_812a, - 0x1641_42af_21dc_f93f, - ]), - c1: Fp::from_raw_unchecked([ - 0xf9b4_a1a8_9598_4db4, - 0xd417_b114_cccf_f748, - 0x6856_301f_c89f_086e, - 0x41c7_7787_8931_e3da, - 0x3556_b155_066a_2105, - 0x00ac_f7d3_25cb_89cf, - ]), - }, - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G2Projective::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G2Affine::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G2Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G2Projective::generator().double(); - assert_eq!(a + (-a), G2Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G2Affine::generator(); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G2Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G2Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G2Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x89f5_50c8_13db_6431, - 0xa50b_e8c4_56cd_8a1a, - 0xa45b_3741_14ca_e851, - 0xbb61_90f5_bf7f_ff63, - 0x970c_a02c_3ba8_0bc7, - 0x02b8_5d24_e840_fbac, - ]), - c1: Fp::from_raw_unchecked([ - 0x6888_bc53_d707_16dc, - 0x3dea_6b41_1768_2d70, - 0xd8f5_f930_500c_a354, - 0x6b5e_cb65_56f5_c155, - 0xc96b_ef04_3477_8ab0, - 0x0508_1505_5150_06ad, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3cf1_ea0d_434b_0f40, - 0x1a0d_c610_e603_e333, - 0x7f89_9561_60c7_2fa0, - 0x25ee_03de_cf64_31c5, - 0xeee8_e206_ec0f_e137, - 0x0975_92b2_26df_ef28, - ]), - c1: Fp::from_raw_unchecked([ - 0x71e8_bb5f_2924_7367, - 0xa5fe_049e_2118_31ce, - 0x0ce6_b354_502a_3896, - 0x93b0_1200_0997_314e, - 0x6759_f3b6_aa5b_42ac, - 0x1569_44c4_dfe9_2bbb, - ]), - }, - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G2Affine::identity().is_torsion_free())); - assert!(bool::from(G2Affine::generator().is_torsion_free())); -} - -#[cfg(feature = "endo")] -#[test] -fn test_mul_by_x() { - // multiplying by `x` a point in G2 is the same as multiplying by - // the equivalent scalar. - let generator = G2Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -Scalar::from(crate::BLS_X) - } else { - Scalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); - - let point = G2Projective::generator() * Scalar::from(42); - assert_eq!(point.mul_by_x(), point * x); -} - -#[cfg(feature = "endo")] -#[test] -fn test_psi() { - let generator = G2Projective::generator(); - - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; - assert!(bool::from(point.is_on_curve())); - - // psi2(P) = psi(psi(P)) - assert_eq!(generator.psi2(), generator.psi().psi()); - assert_eq!(point.psi2(), point.psi().psi()); - // psi(P) is a morphism - assert_eq!(generator.double().psi(), generator.psi().double()); - assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); - // psi(P) behaves in the same way on the same projective point - let mut normalized_point = [G2Affine::identity()]; - G2Projective::batch_normalize(&[point], &mut normalized_point); - let normalized_point = G2Projective::from(normalized_point[0]); - assert_eq!(point.psi(), normalized_point.psi()); - assert_eq!(point.psi2(), normalized_point.psi2()); -} - -#[test] -fn test_clear_cofactor() { - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; - - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G2Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); - - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G2Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G2Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| - // h_eff % q = 0x2b116900400069009a40200040001ffff - let h_eff_modq: [u8; 32] = [ - 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, - 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); - assert_eq!( - cleared_point.clear_cofactor(), - cleared_point.multiply(&h_eff_modq) - ); -} - -#[test] -fn test_batch_normalize() { - let a = G2Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G2Projective::identity() - } - if b_identity { - v[1] = G2Projective::identity() - } - if c_identity { - v[2] = G2Projective::identity() - } - - let mut t = [ - G2Affine::identity(), - G2Affine::identity(), - G2Affine::identity(), - ]; - let expected = [ - G2Affine::from(v[0]), - G2Affine::from(v[1]), - G2Affine::from(v[2]), - ]; - - G2Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs deleted file mode 100644 index 7f16f1b472..0000000000 --- a/bls12_381/src/lib.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! # `bls12_381` -//! -//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic -//! curve construction. -//! -//! * **This implementation has not been reviewed or audited. Use at your own risk.** -//! * This implementation targets Rust `1.36` or later. -//! * This implementation does not require the Rust standard library. -//! * All operations are constant time unless explicitly noted. - -#![no_std] -#![cfg_attr(docsrs, feature(doc_cfg))] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] -#![deny(unsafe_code)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::many_single_char_names)] -// This lint is described at -// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl -// In our library, some of the arithmetic involving extension fields will necessarily -// involve various binary operators, and so this lint is triggered unnecessarily. -#![allow(clippy::suspicious_arithmetic_impl)] - -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(test)] -#[macro_use] -extern crate std; - -#[cfg(test)] -#[cfg(feature = "groups")] -mod tests; - -#[macro_use] -mod util; - -/// Notes about how the BLS12-381 elliptic curve is designed, specified -/// and implemented by this library. -pub mod notes { - pub mod design; - pub mod serialization; -} - -mod scalar; - -pub use scalar::Scalar; - -#[cfg(feature = "groups")] -mod fp; -#[cfg(feature = "groups")] -mod fp2; -#[cfg(feature = "groups")] -mod g1; -#[cfg(feature = "groups")] -mod g2; - -#[cfg(feature = "groups")] -pub use g1::{G1Affine, G1Projective}; -#[cfg(feature = "groups")] -pub use g2::{G2Affine, G2Projective}; - -#[cfg(feature = "groups")] -mod fp12; -#[cfg(feature = "groups")] -mod fp6; - -// The BLS parameter x for BLS12-381 is -0xd201000000010000 -const BLS_X: u64 = 0xd201_0000_0001_0000; -const BLS_X_IS_NEGATIVE: bool = true; - -#[cfg(feature = "pairings")] -mod pairings; - -#[cfg(feature = "pairings")] -pub use pairings::{pairing, Bls12, Gt, MillerLoopResult}; - -#[cfg(all(feature = "pairings", feature = "alloc"))] -pub use pairings::{multi_miller_loop, G2Prepared}; diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs deleted file mode 100644 index d273743b57..0000000000 --- a/bls12_381/src/notes/design.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! # Design of BLS12-381 -//! ## Fixed Generators -//! -//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is -//! safe to use in a cryptographic protocol, we specify some simple, fixed generators. -//! -//! In order to derive these generators, we select the lexicographically smallest -//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate, -//! and then scale the resulting point by the cofactor, such that the result is not the -//! identity. This results in the following fixed generators: -//! -//! 1. $\mathbb{G}_1$ -//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$ -//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$ -//! 2. $\mathbb{G}_2$ -//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$ -//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$ -//! -//! This can be derived using the following sage script: -//! -//! ```text -//! param = -0xd201000000010000 -//! def r(x): -//! return (x**4) - (x**2) + 1 -//! def q(x): -//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x -//! def g1_h(x): -//! return ((x-1)**2) // 3 -//! def g2_h(x): -//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 -//! q = q(param) -//! r = r(param) -//! Fq = GF(q) -//! ec = EllipticCurve(Fq, [0, 4]) -//! def psqrt(v): -//! assert(not v.is_zero()) -//! a = sqrt(v) -//! b = -a -//! if a < b: -//! return a -//! else: -//! return b -//! for x in range(0,100): -//! rhs = Fq(x)^3 + 4 -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec(x, y) * g1_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print("g1 generator: {}".format(p)) -//! break -//! Fq2. = GF(q^2, modulus=[1, 0, 1]) -//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) -//! assert(ec2.order() == (r * g2_h(param))) -//! for x in range(0,100): -//! rhs = (Fq2(x))^3 + (4 * (1 + i)) -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec2(Fq2(x), y) * g2_h(param) -//! if not p.is_zero() and (p * r).is_zero(): -//! print("g2 generator: {}".format(p)) -//! break -//! ``` diff --git a/bls12_381/src/notes/serialization.rs b/bls12_381/src/notes/serialization.rs deleted file mode 100644 index ded752e6e8..0000000000 --- a/bls12_381/src/notes/serialization.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! # BLS12-381 serialization -//! -//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48 -//! bytes in this form. -//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that -//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the -//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$. -//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form. -//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The -//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates. -//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed -//! form (the x-coordinate followed by the y-coordinate) or in compressed form -//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in -//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$ -//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed -//! form. -//! -//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$ -//! encoding should be masked away before the coordinate(s) are interpreted. -//! These bits are used to unambiguously represent the underlying element: -//! * The most significant bit, when set, indicates that the point is in -//! compressed form. Otherwise, the point is in uncompressed form. -//! * The second-most significant bit indicates that the point is at infinity. -//! If this bit is set, the remaining bits of the group element's encoding -//! should be set to zero. -//! * The third-most significant bit is set if (and only if) this point is in -//! compressed form _and_ it is not the point at infinity _and_ its -//! y-coordinate is the lexicographically largest of the two associated with -//! the encoded x-coordinate. diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs deleted file mode 100644 index 6e9cd4b89c..0000000000 --- a/bls12_381/src/pairings.rs +++ /dev/null @@ -1,886 +0,0 @@ -use crate::fp::Fp; -use crate::fp12::Fp12; -use crate::fp2::Fp2; -use crate::fp6::Fp6; -use crate::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use group::Group; -use pairing::{Engine, MultiMillerLoop, PairingCurveAffine}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; - -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -/// Represents results of a Miller loop, one of the most expensive portions -/// of the pairing function. `MillerLoopResult`s cannot be compared with each -/// other until `.final_exponentiation()` is called, which is also expensive. -#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] -#[derive(Copy, Clone, Debug)] -pub struct MillerLoopResult(pub(crate) Fp12); - -impl ConditionallySelectable for MillerLoopResult { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice)) - } -} - -impl MillerLoopResult { - /// This performs a "final exponentiation" routine to convert the result - /// of a Miller loop into an element of `Gt` with help of efficient squaring - /// operation in the so-called `cyclotomic subgroup` of `Fq6` so that - /// it can be compared with other elements of `Gt`. - pub fn final_exponentiation(&self) -> Gt { - #[must_use] - fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) { - let t0 = a.square(); - let t1 = b.square(); - let mut t2 = t1.mul_by_nonresidue(); - let c0 = t2 + t0; - t2 = a + b; - t2 = t2.square(); - t2 -= t0; - let c1 = t2 - t1; - - (c0, c1) - } - // Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography - // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions - // https://eprint.iacr.org/2009/565.pdf - #[must_use] - fn cyclotomic_square(f: Fp12) -> Fp12 { - let mut z0 = f.c0.c0; - let mut z4 = f.c0.c1; - let mut z3 = f.c0.c2; - let mut z2 = f.c1.c0; - let mut z1 = f.c1.c1; - let mut z5 = f.c1.c2; - - let (t0, t1) = fp4_square(z0, z1); - - // For A - z0 = t0 - z0; - z0 = z0 + z0 + t0; - - z1 = t1 + z1; - z1 = z1 + z1 + t1; - - let (mut t0, t1) = fp4_square(z2, z3); - let (t2, t3) = fp4_square(z4, z5); - - // For C - z4 = t0 - z4; - z4 = z4 + z4 + t0; - - z5 = t1 + z5; - z5 = z5 + z5 + t1; - - // For B - t0 = t3.mul_by_nonresidue(); - z2 = t0 + z2; - z2 = z2 + z2 + t0; - - z3 = t2 - z3; - z3 = z3 + z3 + t2; - - Fp12 { - c0: Fp6 { - c0: z0, - c1: z4, - c2: z3, - }, - c1: Fp6 { - c0: z2, - c1: z1, - c2: z5, - }, - } - } - #[must_use] - fn cycolotomic_exp(f: Fp12) -> Fp12 { - let x = BLS_X; - let mut tmp = Fp12::one(); - let mut found_one = false; - for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) { - if found_one { - tmp = cyclotomic_square(tmp) - } else { - found_one = i; - } - - if i { - tmp *= f; - } - } - - tmp.conjugate() - } - - let mut f = self.0; - let mut t0 = f - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map(); - Gt(f.invert() - .map(|mut t1| { - let mut t2 = t0 * t1; - t1 = t2; - t2 = t2.frobenius_map().frobenius_map(); - t2 *= t1; - t1 = cyclotomic_square(t2).conjugate(); - let mut t3 = cycolotomic_exp(t2); - let mut t4 = cyclotomic_square(t3); - let mut t5 = t1 * t3; - t1 = cycolotomic_exp(t5); - t0 = cycolotomic_exp(t1); - let mut t6 = cycolotomic_exp(t0); - t6 *= t4; - t4 = cycolotomic_exp(t6); - t5 = t5.conjugate(); - t4 *= t5 * t2; - t5 = t2.conjugate(); - t1 *= t2; - t1 = t1.frobenius_map().frobenius_map().frobenius_map(); - t6 *= t5; - t6 = t6.frobenius_map(); - t3 *= t0; - t3 = t3.frobenius_map().frobenius_map(); - t3 *= t1; - t3 *= t6; - f = t3 * t4; - - f - }) - // We unwrap() because `MillerLoopResult` can only be constructed - // by a function within this crate, and we uphold the invariant - // that the enclosed value is nonzero. - .unwrap()) - } -} - -impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult { - type Output = MillerLoopResult; - - #[inline] - fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult { - MillerLoopResult(self.0 * rhs.0) - } -} - -impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult); - -/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with -/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$. -/// -/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to -/// keep code and abstractions consistent. -#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] -#[derive(Copy, Clone, Debug, Default)] -pub struct Gt(pub(crate) Fp12); - -impl fmt::Display for Gt { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl ConstantTimeEq for Gt { - fn ct_eq(&self, other: &Self) -> Choice { - self.0.ct_eq(&other.0) - } -} - -impl ConditionallySelectable for Gt { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Gt(Fp12::conditional_select(&a.0, &b.0, choice)) - } -} - -impl Eq for Gt {} -impl PartialEq for Gt { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl Gt { - /// Returns the group identity, which is $1$. - pub fn identity() -> Gt { - Gt(Fp12::one()) - } - - /// Doubles this group element. - pub fn double(&self) -> Gt { - Gt(self.0.square()) - } -} - -impl<'a> Neg for &'a Gt { - type Output = Gt; - - #[inline] - fn neg(self) -> Gt { - // The element is unitary, so we just conjugate. - Gt(self.0.conjugate()) - } -} - -impl Neg for Gt { - type Output = Gt; - - #[inline] - fn neg(self) -> Gt { - -&self - } -} - -impl<'a, 'b> Add<&'b Gt> for &'a Gt { - type Output = Gt; - - #[inline] - fn add(self, rhs: &'b Gt) -> Gt { - Gt(self.0 * rhs.0) - } -} - -impl<'a, 'b> Sub<&'b Gt> for &'a Gt { - type Output = Gt; - - #[inline] - fn sub(self, rhs: &'b Gt) -> Gt { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { - type Output = Gt; - - fn mul(self, other: &'b Scalar) -> Self::Output { - let mut acc = Gt::identity(); - - // This is a simple double-and-add implementation of group element - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in other - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = Gt::conditional_select(&acc, &(acc + self), bit); - } - - acc - } -} - -impl_binops_additive!(Gt, Gt); -impl_binops_multiplicative!(Gt, Scalar); - -impl Sum for Gt -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Group for Gt { - type Scalar = Scalar; - - fn random(rng: &mut R) -> Self { - loop { - let inner = Fp12::random(rng); - - // Not all elements of Fp12 are elements of the prime-order multiplicative - // subgroup. We run the random element through final_exponentiation to obtain - // a valid element, which requires that it is non-zero. - if !bool::from(inner.is_zero()) { - return MillerLoopResult(inner).final_exponentiation(); - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - // pairing(&G1Affine::generator(), &G2Affine::generator()) - Gt(Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1972_e433_a01f_85c5, - 0x97d3_2b76_fd77_2538, - 0xc8ce_546f_c96b_cdf9, - 0xcef6_3e73_66d4_0614, - 0xa611_3427_8184_3780, - 0x13f3_448a_3fc6_d825, - ]), - c1: Fp::from_raw_unchecked([ - 0xd263_31b0_2e9d_6995, - 0x9d68_a482_f779_7e7d, - 0x9c9b_2924_8d39_ea92, - 0xf480_1ca2_e131_07aa, - 0xa16c_0732_bdbc_b066, - 0x083c_a4af_ba36_0478, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x59e2_61db_0916_b641, - 0x2716_b6f4_b23e_960d, - 0xc8e5_5b10_a0bd_9c45, - 0x0bdb_0bd9_9c4d_eda8, - 0x8cf8_9ebf_57fd_aac5, - 0x12d6_b792_9e77_7a5e, - ]), - c1: Fp::from_raw_unchecked([ - 0x5fc8_5188_b0e1_5f35, - 0x34a0_6e3a_8f09_6365, - 0xdb31_26a6_e02a_d62c, - 0xfc6f_5aa9_7d9a_990b, - 0xa12f_55f5_eb89_c210, - 0x1723_703a_926f_8889, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x9358_8f29_7182_8778, - 0x43f6_5b86_11ab_7585, - 0x3183_aaf5_ec27_9fdf, - 0xfa73_d7e1_8ac9_9df6, - 0x64e1_76a6_a64c_99b0, - 0x179f_a78c_5838_8f1f, - ]), - c1: Fp::from_raw_unchecked([ - 0x672a_0a11_ca2a_ef12, - 0x0d11_b9b5_2aa3_f16b, - 0xa444_12d0_699d_056e, - 0xc01d_0177_221a_5ba5, - 0x66e0_cede_6c73_5529, - 0x05f5_a71e_9fdd_c339, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xd30a_88a1_b062_c679, - 0x5ac5_6a5d_35fc_8304, - 0xd0c8_34a6_a81f_290d, - 0xcd54_30c2_da37_07c7, - 0xf0c2_7ff7_8050_0af0, - 0x0924_5da6_e2d7_2eae, - ]), - c1: Fp::from_raw_unchecked([ - 0x9f2e_0676_791b_5156, - 0xe2d1_c823_4918_fe13, - 0x4c9e_459f_3c56_1bf4, - 0xa3e8_5e53_b9d3_e3c1, - 0x820a_121e_21a7_0020, - 0x15af_6183_41c5_9acc, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7c95_658c_2499_3ab1, - 0x73eb_3872_1ca8_86b9, - 0x5256_d749_4774_34bc, - 0x8ba4_1902_ea50_4a8b, - 0x04a3_d3f8_0c86_ce6d, - 0x18a6_4a87_fb68_6eaa, - ]), - c1: Fp::from_raw_unchecked([ - 0xbb83_e71b_b920_cf26, - 0x2a52_77ac_92a7_3945, - 0xfc0e_e59f_94f0_46a0, - 0x7158_cdf3_7860_58f7, - 0x7cc1_061b_82f9_45f6, - 0x03f8_47aa_9fdb_e567, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8078_dba5_6134_e657, - 0x1cd7_ec9a_4399_8a6e, - 0xb1aa_599a_1a99_3766, - 0xc9a0_f62f_0842_ee44, - 0x8e15_9be3_b605_dffa, - 0x0c86_ba0d_4af1_3fc2, - ]), - c1: Fp::from_raw_unchecked([ - 0xe80f_f2a0_6a52_ffb1, - 0x7694_ca48_721a_906c, - 0x7583_183e_03b0_8514, - 0xf567_afdd_40ce_e4e2, - 0x9a6d_96d2_e526_a5fc, - 0x197e_9f49_861f_2242, - ]), - }, - }, - }) - } - - fn is_identity(&self) -> Choice { - self.ct_eq(&Self::identity()) - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))] -#[derive(Clone, Debug)] -/// This structure contains cached computations pertaining to a $\mathbb{G}_2$ -/// element as part of the pairing function (specifically, the Miller loop) and -/// so should be computed whenever a $\mathbb{G}_2$ element is being used in -/// multiple pairings or is otherwise known in advance. This should be used in -/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop) -/// function provided by this crate. -/// -/// Requires the `alloc` and `pairing` crate features to be enabled. -pub struct G2Prepared { - infinity: Choice, - coeffs: Vec<(Fp2, Fp2, Fp2)>, -} - -#[cfg(feature = "alloc")] -impl From for G2Prepared { - fn from(q: G2Affine) -> G2Prepared { - struct Adder { - cur: G2Projective, - base: G2Affine, - coeffs: Vec<(Fp2, Fp2, Fp2)>, - } - - impl MillerLoopDriver for Adder { - type Output = (); - - fn doubling_step(&mut self, _: Self::Output) -> Self::Output { - let coeffs = doubling_step(&mut self.cur); - self.coeffs.push(coeffs); - } - fn addition_step(&mut self, _: Self::Output) -> Self::Output { - let coeffs = addition_step(&mut self.cur, &self.base); - self.coeffs.push(coeffs); - } - fn square_output(_: Self::Output) -> Self::Output {} - fn conjugate(_: Self::Output) -> Self::Output {} - fn one() -> Self::Output {} - } - - let is_identity = q.is_identity(); - let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity); - - let mut adder = Adder { - cur: G2Projective::from(q), - base: q, - coeffs: Vec::with_capacity(68), - }; - - miller_loop(&mut adder); - - assert_eq!(adder.coeffs.len(), 68); - - G2Prepared { - infinity: is_identity, - coeffs: adder.coeffs, - } - } -} - -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(all(feature = "pairings", feature = "alloc"))))] -/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms -/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ -/// -/// Requires the `alloc` and `pairing` crate features to be enabled. -pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult { - struct Adder<'a, 'b, 'c> { - terms: &'c [(&'a G1Affine, &'b G2Prepared)], - index: usize, - } - - impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> { - type Output = Fp12; - - fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output { - let index = self.index; - for term in self.terms { - let either_identity = term.0.is_identity() | term.1.infinity; - - let new_f = ell(f, &term.1.coeffs[index], term.0); - f = Fp12::conditional_select(&new_f, &f, either_identity); - } - self.index += 1; - - f - } - fn addition_step(&mut self, mut f: Self::Output) -> Self::Output { - let index = self.index; - for term in self.terms { - let either_identity = term.0.is_identity() | term.1.infinity; - - let new_f = ell(f, &term.1.coeffs[index], term.0); - f = Fp12::conditional_select(&new_f, &f, either_identity); - } - self.index += 1; - - f - } - fn square_output(f: Self::Output) -> Self::Output { - f.square() - } - fn conjugate(f: Self::Output) -> Self::Output { - f.conjugate() - } - fn one() -> Self::Output { - Fp12::one() - } - } - - let mut adder = Adder { terms, index: 0 }; - - let tmp = miller_loop(&mut adder); - - MillerLoopResult(tmp) -} - -/// Invoke the pairing function without the use of precomputation and other optimizations. -#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] -pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { - struct Adder { - cur: G2Projective, - base: G2Affine, - p: G1Affine, - } - - impl MillerLoopDriver for Adder { - type Output = Fp12; - - fn doubling_step(&mut self, f: Self::Output) -> Self::Output { - let coeffs = doubling_step(&mut self.cur); - ell(f, &coeffs, &self.p) - } - fn addition_step(&mut self, f: Self::Output) -> Self::Output { - let coeffs = addition_step(&mut self.cur, &self.base); - ell(f, &coeffs, &self.p) - } - fn square_output(f: Self::Output) -> Self::Output { - f.square() - } - fn conjugate(f: Self::Output) -> Self::Output { - f.conjugate() - } - fn one() -> Self::Output { - Fp12::one() - } - } - - let either_identity = p.is_identity() | q.is_identity(); - let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity); - let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity); - - let mut adder = Adder { - cur: G2Projective::from(q), - base: q, - p, - }; - - let tmp = miller_loop(&mut adder); - let tmp = MillerLoopResult(Fp12::conditional_select( - &tmp, - &Fp12::one(), - either_identity, - )); - tmp.final_exponentiation() -} - -trait MillerLoopDriver { - type Output; - - fn doubling_step(&mut self, f: Self::Output) -> Self::Output; - fn addition_step(&mut self, f: Self::Output) -> Self::Output; - fn square_output(f: Self::Output) -> Self::Output; - fn conjugate(f: Self::Output) -> Self::Output; - fn one() -> Self::Output; -} - -/// This is a "generic" implementation of the Miller loop to avoid duplicating code -/// structure elsewhere; instead, we'll write concrete instantiations of -/// `MillerLoopDriver` for whatever purposes we need (such as caching modes). -fn miller_loop(driver: &mut D) -> D::Output { - let mut f = D::one(); - - let mut found_one = false; - for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) { - if !found_one { - found_one = i; - continue; - } - - f = driver.doubling_step(f); - - if i { - f = driver.addition_step(f); - } - - f = D::square_output(f); - } - - f = driver.doubling_step(f); - - if BLS_X_IS_NEGATIVE { - f = D::conjugate(f); - } - - f -} - -fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 { - let mut c0 = coeffs.0; - let mut c1 = coeffs.1; - - c0.c0 *= p.y; - c0.c1 *= p.y; - - c1.c0 *= p.x; - c1.c1 *= p.x; - - f.mul_by_014(&coeffs.2, &c1, &c0) -} - -fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) { - // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - let tmp0 = r.x.square(); - let tmp1 = r.y.square(); - let tmp2 = tmp1.square(); - let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; - let tmp3 = tmp3 + tmp3; - let tmp4 = tmp0 + tmp0 + tmp0; - let tmp6 = r.x + tmp4; - let tmp5 = tmp4.square(); - let zsquared = r.z.square(); - r.x = tmp5 - tmp3 - tmp3; - r.z = (r.z + r.y).square() - tmp1 - zsquared; - r.y = (tmp3 - r.x) * tmp4; - let tmp2 = tmp2 + tmp2; - let tmp2 = tmp2 + tmp2; - let tmp2 = tmp2 + tmp2; - r.y -= tmp2; - let tmp3 = tmp4 * zsquared; - let tmp3 = tmp3 + tmp3; - let tmp3 = -tmp3; - let tmp6 = tmp6.square() - tmp0 - tmp5; - let tmp1 = tmp1 + tmp1; - let tmp1 = tmp1 + tmp1; - let tmp6 = tmp6 - tmp1; - let tmp0 = r.z * zsquared; - let tmp0 = tmp0 + tmp0; - - (tmp0, tmp3, tmp6) -} - -fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { - // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - let zsquared = r.z.square(); - let ysquared = q.y.square(); - let t0 = zsquared * q.x; - let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; - let t2 = t0 - r.x; - let t3 = t2.square(); - let t4 = t3 + t3; - let t4 = t4 + t4; - let t5 = t4 * t2; - let t6 = t1 - r.y - r.y; - let t9 = t6 * q.x; - let t7 = t4 * r.x; - r.x = t6.square() - t5 - t7 - t7; - r.z = (r.z + t2).square() - zsquared - t3; - let t10 = q.y + r.z; - let t8 = (t7 - r.x) * t6; - let t0 = r.y * t5; - let t0 = t0 + t0; - r.y = t8 - t0; - let t10 = t10.square() - ysquared; - let ztsquared = r.z.square(); - let t10 = t10 - ztsquared; - let t9 = t9 + t9 - t10; - let t10 = r.z + r.z; - let t6 = -t6; - let t1 = t6 + t6; - - (t10, t1, t9) -} - -impl PairingCurveAffine for G1Affine { - type Pair = G2Affine; - type PairingResult = Gt; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - pairing(self, other) - } -} - -impl PairingCurveAffine for G2Affine { - type Pair = G1Affine; - type PairingResult = Gt; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - pairing(other, self) - } -} - -/// A [`pairing::Engine`] for BLS12-381 pairing operations. -#[cfg_attr(docsrs, doc(cfg(feature = "pairings")))] -#[derive(Clone, Debug)] -pub struct Bls12; - -impl Engine for Bls12 { - type Fr = Scalar; - type G1 = G1Projective; - type G1Affine = G1Affine; - type G2 = G2Projective; - type G2Affine = G2Affine; - type Gt = Gt; - - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - pairing(p, q) - } -} - -impl pairing::MillerLoopResult for MillerLoopResult { - type Gt = Gt; - - fn final_exponentiation(&self) -> Self::Gt { - self.final_exponentiation() - } -} - -impl MultiMillerLoop for Bls12 { - type G2Prepared = G2Prepared; - type Result = MillerLoopResult; - - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { - multi_miller_loop(terms) - } -} - -#[test] -fn test_gt_generator() { - assert_eq!( - Gt::generator(), - pairing(&G1Affine::generator(), &G2Affine::generator()) - ); -} - -#[test] -fn test_bilinearity() { - use crate::Scalar; - - let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); - let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); - let c = a * b; - - let g = G1Affine::from(G1Affine::generator() * a); - let h = G2Affine::from(G2Affine::generator() * b); - let p = pairing(&g, &h); - - assert!(p != Gt::identity()); - - let expected = G1Affine::from(G1Affine::generator() * c); - - assert_eq!(p, pairing(&expected, &G2Affine::generator())); - assert_eq!( - p, - pairing(&G1Affine::generator(), &G2Affine::generator()) * c - ); -} - -#[test] -fn test_unitary() { - let g = G1Affine::generator(); - let h = G2Affine::generator(); - let p = -pairing(&g, &h); - let q = pairing(&g, &-h); - let r = pairing(&-g, &h); - - assert_eq!(p, q); - assert_eq!(q, r); -} - -#[cfg(feature = "alloc")] -#[test] -fn test_multi_miller_loop() { - let a1 = G1Affine::generator(); - let b1 = G2Affine::generator(); - - let a2 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), - ); - let b2 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), - ); - - let a3 = G1Affine::identity(); - let b3 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), - ); - - let a4 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), - ); - let b4 = G2Affine::identity(); - - let a5 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(), - ); - let b5 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(), - ); - - let b1_prepared = G2Prepared::from(b1); - let b2_prepared = G2Prepared::from(b2); - let b3_prepared = G2Prepared::from(b3); - let b4_prepared = G2Prepared::from(b4); - let b5_prepared = G2Prepared::from(b5); - - let expected = pairing(&a1, &b1) - + pairing(&a2, &b2) - + pairing(&a3, &b3) - + pairing(&a4, &b4) - + pairing(&a5, &b5); - - let test = multi_miller_loop(&[ - (&a1, &b1_prepared), - (&a2, &b2_prepared), - (&a3, &b3_prepared), - (&a4, &b4_prepared), - (&a5, &b5_prepared), - ]) - .final_exponentiation(); - - assert_eq!(expected, test); -} diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs deleted file mode 100644 index c9c385d2df..0000000000 --- a/bls12_381/src/scalar.rs +++ /dev/null @@ -1,1174 +0,0 @@ -//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ -//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -use core::convert::TryFrom; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; - -use ff::{Field, PrimeField}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Scalar` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Scalar(pub(crate) [u64; 4]); - -impl fmt::Debug for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl fmt::Display for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl From for Scalar { - fn from(val: u64) -> Scalar { - Scalar([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Scalar { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl PartialEq for Scalar { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Scalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Scalar([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - ]) - } -} - -/// Constant representing the modulus -/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -const MODULUS: Scalar = Scalar([ - 0xffff_ffff_0000_0001, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -const MODULUS_BYTES: [u8; 32] = [ - 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, 0x53, - 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, 0xed, 0x73, -]; - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 255; - -// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: Scalar = Scalar([ - 0x0000_000e_ffff_fff1, - 0x17e3_63d3_0018_9c0f, - 0xff9c_5787_6f84_57b0, - 0x3513_3220_8fc5_a8c4, -]); - -impl<'a> Neg for &'a Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - self.neg() - } -} - -impl Neg for Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - -&self - } -} - -impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn sub(self, rhs: &'b Scalar) -> Scalar { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn add(self, rhs: &'b Scalar) -> Scalar { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn mul(self, rhs: &'b Scalar) -> Scalar { - self.mul(rhs) - } -} - -impl_binops_additive!(Scalar, Scalar); -impl_binops_multiplicative!(Scalar, Scalar); - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xffff_fffe_ffff_ffff; - -/// R = 2^256 mod q -const R: Scalar = Scalar([ - 0x0000_0001_ffff_fffe, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, -]); - -/// R^2 = 2^512 mod q -const R2: Scalar = Scalar([ - 0xc999_e990_f3f2_9c6d, - 0x2b6c_edcb_8792_5c23, - 0x05d3_1496_7254_398f, - 0x0748_d9d9_9f59_ff11, -]); - -/// R^3 = 2^768 mod q -const R3: Scalar = Scalar([ - 0xc62c_1807_439b_73af, - 0x1b3e_0d18_8cf0_6990, - 0x73d1_3c71_c7b5_f418, - 0x6e2a_5bb9_c8db_33e9, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 32; - -/// GENERATOR^t where t * 2^s + 1 = q -/// with t odd. In other words, this -/// is a 2^s root of unity. -/// -/// `GENERATOR = 7 mod q` is a generator -/// of the q - 1 order multiplicative -/// subgroup. -const ROOT_OF_UNITY: Scalar = Scalar([ - 0xb9b5_8d8c_5f0e_466a, - 0x5b1b_4c80_1819_d7ec, - 0x0af5_3ae3_52a3_1e64, - 0x5bf3_adda_19e9_b27b, -]); - -impl Default for Scalar { - #[inline] - fn default() -> Self { - Self::zero() - } -} - -impl Scalar { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Scalar { - Scalar([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Scalar { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Scalar { - // TODO: This can be achieved more efficiently with a bitshift. - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Scalar`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Scalar([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); - tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Scalar` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// a `Scalar` by reducing by the modulus. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { - Scalar::from_u512([ - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Scalar { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multiplied by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Scalar` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Scalar(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Scalar { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - // = self^6104339283789297388802252303364915521546564123189034618274734669823 - let w = self.pow_vartime(&[ - 0x7fff_2dff_7fff_ffff, - 0x04d0_ec02_a9de_d201, - 0x94ce_bea4_199c_ec04, - 0x0000_0000_39f6_d3a9, - ]); - - let mut v = S; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Scalar::one()); - let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Scalar::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp *= self; - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - /// - /// **This operation is variable time with respect - /// to the exponent.** If the exponent is fixed, - /// this operation is effectively constant time. - pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Scalar, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t0 = self.square(); - let mut t1 = t0 * self; - let mut t16 = t0.square(); - let mut t6 = t16.square(); - let mut t5 = t6 * t0; - t0 = t6 * t16; - let mut t12 = t5 * t16; - let mut t2 = t6.square(); - let mut t7 = t5 * t6; - let mut t15 = t0 * t5; - let mut t17 = t12.square(); - t1 *= t17; - let mut t3 = t7 * t2; - let t8 = t1 * t17; - let t4 = t8 * t2; - let t9 = t8 * t7; - t7 = t4 * t5; - let t11 = t4 * t17; - t5 = t9 * t17; - let t14 = t7 * t15; - let t13 = t11 * t12; - t12 = t11 * t17; - t15 *= &t12; - t16 *= &t15; - t3 *= &t16; - t17 *= &t3; - t0 *= &t17; - t6 *= &t0; - t2 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t17; - square_assign_multi(&mut t0, 9); - t0 *= &t16; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 7); - t0 *= &t14; - square_assign_multi(&mut t0, 7); - t0 *= &t13; - square_assign_multi(&mut t0, 10); - t0 *= &t12; - square_assign_multi(&mut t0, 9); - t0 *= &t11; - square_assign_multi(&mut t0, 8); - t0 *= &t8; - square_assign_multi(&mut t0, 8); - t0 *= self; - square_assign_multi(&mut t0, 14); - t0 *= &t9; - square_assign_multi(&mut t0, 10); - t0 *= &t8; - square_assign_multi(&mut t0, 15); - t0 *= &t7; - square_assign_multi(&mut t0, 10); - t0 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t5; - square_assign_multi(&mut t0, 16); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 7); - t0 *= &t4; - square_assign_multi(&mut t0, 9); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline(always)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts `rhs` from `self`, returning the result. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Scalar([d0, d1, d2, d3]) - } - - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates `self`. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl From for [u8; 32] { - fn from(value: Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -impl<'a> From<&'a Scalar> for [u8; 32] { - fn from(value: &'a Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -impl Field for Scalar { - fn random(rng: &mut R) -> Self { - let mut buf = [0; 64]; - rng.fill_bytes(&mut buf); - Self::from_bytes_wide(&buf) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn is_zero(&self) -> bool { - self.ct_eq(&Self::zero()).into() - } - - #[must_use] - fn square(&self) -> Self { - self.square() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } - - fn invert(&self) -> CtOption { - self.invert() - } - - fn sqrt(&self) -> CtOption { - self.sqrt() - } -} - -impl PrimeField for Scalar { - type Repr = [u8; 32]; - type ReprEndianness = byteorder::LittleEndian; - - fn from_repr(r: Self::Repr) -> Option { - let res = Self::from_bytes(&r); - if res.is_some().into() { - Some(res.unwrap()) - } else { - None - } - } - - fn to_repr(&self) -> Self::Repr { - self.to_bytes() - } - - fn is_odd(&self) -> bool { - self.to_bytes()[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS_BYTES - } - - const NUM_BITS: u32 = MODULUS_BITS; - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -#[test] -fn test_inv() { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[cfg(feature = "std")] -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Scalar::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Scalar::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Scalar::zero(), Scalar::zero()); - assert_eq!(Scalar::one(), Scalar::one()); - assert_eq!(R2, R2); - - assert!(Scalar::zero() != Scalar::one()); - assert!(Scalar::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Scalar::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Scalar::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ] - ); - - assert_eq!( - (-&Scalar::one()).to_bytes(), - [ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::zero() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::one() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!(bool::from( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_some() - )); - - // modulus is invalid - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - - // Anything larger than the modulus is invalid - assert!(bool::from( - Scalar::from_bytes(&[ - 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 - ]) - .is_none() - )); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Scalar::zero(), - Scalar::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Scalar::from_bytes_wide(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Scalar::one(), - Scalar::from_bytes_wide(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Scalar([ - 0xc62c_1805_439b_73b1, - 0xc2b9_551e_8ced_218e, - 0xda44_ec81_daf9_a422, - 0x5605_aa60_1c16_2e79, - ]), - Scalar::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Scalar::zero(), -&Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); -} - -#[cfg(test)] -const LARGEST: Scalar = Scalar([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Scalar([ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]) - ); - - let mut tmp = LARGEST; - tmp += &Scalar([1, 0, 0, 0]); - - assert_eq!(tmp, Scalar::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Scalar([1, 0, 0, 0])); - - let tmp = -&Scalar::zero(); - assert_eq!(tmp, Scalar::zero()); - let tmp = -&Scalar([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Scalar::zero()); - - let mut tmp = Scalar::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert!(bool::from(Scalar::zero().invert().is_none())); - assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); - assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Scalar::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let q_minus_2 = [ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&q_minus_2); - r3 = r3.pow(&q_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - { - assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); - } - - let mut square = Scalar([ - 0x46cd_85a5_f273_077e, - 0x1d30_c47d_d68f_c735, - 0x77f6_56f6_0bec_a0eb, - 0x494a_a01b_df32_468d, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if bool::from(square_root.is_none()) { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Scalar::one(); - } - - assert_eq!(49, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Scalar::from_raw([ - 0x0001_ffff_fffd, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, - ]), - Scalar::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); - - assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); -} - -#[test] -fn test_double() { - let a = Scalar::from_raw([ - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562, - ]); - - assert_eq!(a.double(), a + a); -} diff --git a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat deleted file mode 100644 index ea8cd67652d133010e79df8488452450b6f5cb17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48000 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 Rm?nepfGSPSF*XppvfIk?3d#Tg diff --git a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 86abfba945c7b701750d637bffe6701330e10e88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96000 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%0A9l>2- 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 diff --git a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat deleted file mode 100644 index a40bbe251d90e576060adb7eaa2456197878804c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96000 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 diff --git a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 92e4bc528e8937a311b502e4940e5e363a1f7c57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192000 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�uc*gWwz@d=S2~bA~LrFNXzUL(>hGG#V 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%0DJH z2`)#|uM*o9m5qzl4g3nMFu&y4D*`;d8yq}X z6SD|!kTx07ZmCp;pp@`MzxY@(S}{%eVLGR9)|Sex7q}$X$CbH|TN+y0qhN(8)A`3& z6-#~gvZZ_uXeO!nFTyXpRW)-S$oXiinHIoP6*B&LP^(}=v!)zfE~3bei&km>Zs@{@ z9Zg<9lz_zjY>$hKMu=DlJ=_x;i+zL*03I&uw&I4!%4n1>x98J5GbL*d+i5jfOT)=h+Rh zy(k5@dwvB#UH~1q`H3|P0bxX$d%vrtT8${Dqll|){KEO(6Nu>yA4G0p6*1oxt??b< zIZ`Nq!VyE1fqEke55zO~qn+^6nbDna3@+%zjq&Psyr9>2>5LWk>_WII7Ans`PDabb?3?+fr(`m2{QGJosxj`SM=?&S0!^0A)!uZ9}f%c~2hY{sT%J{y3-*tR@cQW#( z2zTn=Yy&fje@Ye?Qoi|U`c+S{+AJA{0S*)+8tE0_CdqYDJ7~zRykc{ggL`}o0JXdF zJt^QkO0-P$1Q{Xl5m ze-dAQe;n@q>GyJn7=#uAZ)eqdQ%y4*gQaQE4=vyAi5Z%79R z2+s!{Zf#C_7XC(6?&Rpwpyxjp%c@E>lPXehRJUvG+N!%$@2f}={TzdRSwet#6$4Sm zRIV|JJguPUff}--H7+xHR?hr*by$m>9u#aq_fdEAeWleGbO~9X)<4EL5(x&Akxk1@ z&`S5qpKjcDLs4XGlvbL3)IBo*O;I&`(t%P&IrVu@VoMTehYwf72u_d9tRN8hx5Kq-1E0TTC{&5-kq7u*ad}#le|z49n`H z0ulp#gO{HYbhI*3j{kZ#FZsPmMcJfKOTXVooCKL@1uO>AhC&f(iS}j-=CQ*$)^$Kt z2BZ8cCV^~HVv%D3iRiFf+8d)#zDG>vZ_-%a31Sdq13&=PXbUmm%pGYhIq@3W`_e56 zl&<$(y5scgzqBPmZF+x**z1l3bx{vK6=5t0!f@E9bM_zaNVvUr<*2n`7U$b^=6)wp zDNuH81G?u}u%e962mOe59szqt7IaUKNL?^hxzqwFBQOj4YKL8-P+GhOn9m9P&aejp zrv4XKa8C?K<@>*yG+rV&1~I+(t+y)Q=cL}9>rEYHcEl>NU6a;g=a=cB@UnKh4-z}N zn&45|bDS7&nL6)H0YOVsRnRkl+9hZ{fR@*SfM+ zLHlCroIv%>85|C2TjkkJ*tuJL3h*d3!q#UEbV0vsI7*rrhk0Gl8%5_j3A|(r$DL4^ zpc;vO8GdzDGDNofq&I5dW_p`8UR;DQ1SWCg)a#PaY0u%v(3Mzzg;C^~DsWnR*PMXv z0k&S$HH?2koGzEa!MN6uzYEEdIyKB4H^7tpXMZ%{p&pOyHK(dnvN@=F*hz6a7*I(eHF>1Wj3;$1wRRg!r|=$; z#CdBS^(w6xBg$Gh-I!j|XR!-cPj?1`wrTRc7As+(5p6gb%pl(5K0GFS%O7hdIc-2M zEv;z(s8Lty65-JMR%A;ch5XPTdAD!_4J(9zk2n$IBX{`m>0~ZXdGAg)8@IF|2|;~B z^zUm!U;_MIY_H z6my>A$&BXR$Ppbisvbq4&}dJuv`pS}aliJc!{=Fr zfe=Oc(HB=@o*;7S5|dkM#ZtO$U3R}RX+t5pSyVHm<4)6WVt7Mf%srAVGj$cDZPiV) ztd?G6IR3GK1q$U91Q3WF8CykU`4+_f4ib-6qRteaM}SE*>Mb-Ch=7 zze_#%pB<`9zQbe6=l99X5M#D%w&>9=6MQ*Ql^fP8AaCUi52*#77MsJ)etq6Vi*8|| zp~sW5rpx}=<67J3n9VJFkm=pisGpj_>T_WV-sk594i3CSQy-e z9`7m%(1QOI&)S(e<@V%H7?9F!iI%T~-E;CNG_Rbn*47$VuRBXuem61e5xZ)+9clCUpLsaz^4u5BOYU-9Skf=J^zl3o1^d#~e>}RPIS-Zs?Tl=$ z=lVF>2n&1r#qc1iF)-y`r&1=b1HT29L+7M$N#_jDmlTc64`Tp)2GDU5fgsX zXQd9L%xPP7k~ktxPDGd|N&xU)=ob-VLL75UK3D*%zr?e5!@X18q=5T=dA|a&-F->GM{PtjDm{pk5{2b_)a;i~lpdZX12e`$q-PC!0|}K`*fU7RYyB1) z+YINrYp6qr>pNi2Bf)Ci(A#eo{l|1h5MuDQ8kNO z{zc-8`uYDB0urFaSNJ{{tya{I2F81bV^i!}129?M3taRp z%+bu(7`Zp$%uOUECsZn@I}^2|OS-YC|1wA1@BtRN0%}Q9T+LB1d^d+FHfgt?CKb&0 z0gcnVL$o&(OsHQKfKkd9YAJ>wI{Ej^or+={iAZqMU#=RX`%BJ3EqF?8QUmM60LF=8 zt;rt6V5mLoQmD`d?ow#ib&>!ALSF!aC`KT?wuSevX35DbkT0SK<&KEG1ZKX(eK;l3 zJcH}UGQ8VZzz*YaMc3Cb<|E7WydxYv+0b-clF#NdWQ~B_Cy3yD4^7kkQ>6exw-|DR zW-`R+l}aC!I~F_W1q63DZffT>S00O z-VquZO|2ZXE0?>fqgf_dFhhO@Lw3WlkGpK`bDB{Q8NGrlc$%N1qe{Wi|Ky48JXs*k zcd^))if1gntwc)v-EGUu+`9;IA-CDNT7y`F!90pQzTy#y{1h#2Ol+1OLs=T0Qr{-*^tf9$ph z91kT&9W7fEk$5_pM{m~f^gVEt5qt>Jx0|ns{eM9o9(XZNab2oNaG8MfT&>osv+IPZ zTmxd_J8R(edYe-q_&ip(07?BX4Tlvv@_?i(vm2l@^Z(k&T+X|1QV{>U3SGK4+?r~maoJ0W#G6f7+GvNPV18yX-t)tDddMN1&_Mv(4BDAS%vpD|I#8MGl&BW?7lRp zdnn*8VA~NE1Ov_tS1T;G`1!$YxUZMVZsXoDBqwX#~f{-*c8kY>5T3Sf!pL=8SNO+hy5 z^-g3GMJ6B_>J`%z6BUtBIxkMTsevKW^+YC!Y&wesEwDcv17JQmv85f~dqm^MZWG#v z?g$>biw6}9ghRF-X%EoPw2!pV3x4sH5-N(f0sVZY#G{=Y*xVspzR z(tE?m5{^K5=){ND*m-88%=9I%;~Zq0 z-x)NrL?wroMcfD_=;9RZ_68?V4b$p#zd%A+Ed*pq1iCgYM32A!7-}HAi^h^J&7Dbm z97M-6!I8h-y6p4MsaVo~2YfQ#BhI%Qzh;WAvp+KS05ow~qey~o?h5*CFKftnUa@Ss86 zvm$<=5G^LC!GNJxtPqI3@b892I!3PLHBB=_I(GUTd!DxxLx>|7TmF?)a(@>cVpBCY z1y#JbKsp$c-0VJTTpuUTI|bVY8{RYj31R67Cby;LTO&P)x?8& zA19gKZ7G5e@8I;%wA9IkFh9lhH*0z{T#tUHgp?AZ`0@lM2dj1D*h3H)blGXpm;Nih z=)FvL6%g1GUV_C636S)%LgcTJd*jGclnY+h<}wkEKDGDFwqvS2EX4tbL#UTinB#SN~9f_V3{mT}_0v3fFCNi<`6oD~b~ZdHbgwD~x9j`lcG z5YsFcV1+`UpM$`v6e7}Nqb{4q&F6$`4?M{$RTEh^>|swAt9F50CTVHjX-hKd4A_9H zi5JK|!Suxcxh!2=t8&55nG-qS4>@ladNSlGOD*$g6vXu2j9$k_)(F>D}&A$^R<9hFfK5G;nph_0g5HqE0;BId7UM&_%4Jduj z8Y3*j(3~}wm$&uagq>Wyng}m5bESs{ylhQLf1G%d3G5=7*zH#pv8y%8mqdY0YLk_6 z2Sit`^(u^h_3_|=k?N+4X8zELU@dA)at`ERV(`2gX$3MX^ISGPpCugBa5W)Z$I@%sjPOQ zO|aYk;kpU*hN3Ut2}k^D19iqbotu71_BRF{C+f`@Av@pqO<@be)S}O9kWZvGZH@6E zy?A%<%$lUD1Zm7x353J&mM9+Q-jt*Sx~ZV_BIBR40uD0Lo}pbZ%XeN4C4=GS31miG zp`Rr42mwxQ^tsHu?vw*%-lYfHd;-dR;cv+p=mPw?>sK%M&p=v2lM2ap>}>HG#&d7l z4eo_H#N16OBdp0ZeayefYu_6nw_9V@yvu&iNU05Ns{$Mr*0z(*}GiRxyY*A}n^p~&Srw}{Rqyh*}n1aMUAn9!NeEeR+pq7*Iv1s6uUH(j}f z&<>T8!A=aei93#Q8?Y5=d4vO3Qd1laYJ;%CSafil-wMCIgNJL+58ctu7$w;2rv^tq z`s5fGXH_c;vgPKKGAFri!$G`TTX(DHaM8(8-ExGj-_Pih5ZF$AY9&MocUDc5%XBQm zwZJ%v;JC~HsZvc%*TM0oFwqFjz}8QM6=ZRJY+f`O7BjgSMM6EQ_#||=9%egH4oh3o z?DnZqBpL=Z@gcPhT88tiwwi0>{@zX*n?0Mt4aGweb`lrf5=e-MDmVg~bpzqYD}T%A z6=oFS^$|t!JrebAwj3~-&--B!1@$z_4MRU^JCTwGDk;X3b=M4+B~+B8tYOw{9(a*e zB0D45x_JKU75BUpDS=r?~kjeSK4R~$6 zI{wcqR5la}T-%^avm`=Vj^*bm;p=YGJJ&adQtl0WOX&e$DaX(ZhplXB5sOh~a?P zR3gm>Tt0>8O)BN_Q+f%0l9qqEM^49*g(}J|6UC3aJu|iH5*l-b{OG4{1+=wR`fB;F zrm5Xm7{%9x^!wvn6{EOy^o6qlsg>(ephW=uzIWM=Z$&HZZx2;plt zr2z@<`gZB7VuFD3dr~OfSDJQt+yCgZnG31u{ z18ABJ@<2DJhXLSfF=K2L2i3ms1+0^n_h8+?DUlz@*jce47JE3LYaSrsCO%_6YasPq6N>w6 zR=5Id35i-l8U7PPH);LWr;v#*(0kvo2{Z}y;d>{Uyy*&SAw~VJo9TADlAwjow*50% zF}mSo5V4u%G$%CssV{;sU+fL_kxvf#%#Nso%WAmG{K4h3NbVW696cv#CwXvs_ACGkZ&8$FCC>JbJw>iNeIG)<8?3sbKVVM@4Bpd#(4A{v z8ZV`Ou(Tr2@lJ8_Ar4h|NjIq44vL_2#kIG9*)=DXZ;Gnfs*~KH1M&SlVEh?5)>ut0 zdaY^*>OG<#Oj@eD5sJ z!RdBTWThXIq68hfj*QqNmA{R8F^h2_hE?>RvVIDD2S7dP^PgsGPDxA)qTEN4Ol(+< zlm9`z=1@BI!BXvPdo`X*z^3BYZ)R@^!=_MS1rO!;fw?U!z880L?A_VM$z)5PymH^T zEz>5)F>dm?tztz-NtFy$MwMvp#+WQ0wz86yr?l^1q= zJTalpk*}@2vdXW^nRwpB@l99t**fJ%rxX`50QE05)UYhm+l3bHRE78{6=L7awnxv} z?0*QJcbFXL&%CrQgXo;Vi*cGK8VBL50U|8Yt+*NdId@UuVGb$R_YU<)d=lA@>(Xj+ z$FyH6%;XSz8vu@CA!osdat0=!1nFxEs>)2pNeJ9ki2QR3AcJ=+EiYMA&6?OE3A>VW zJ%jB-#la=EU6gq(G6F-;2Yp4XU!=Vwp`A4lZbeI)xS$@`K}yQ*}&WQp>306gvycX#`dw{6TuP4)Yup8-^{8--PTntxd*Zv+_ z3Ko+r4I*h;2+mg|hZhRs!jo;%3-i&BVt`6!Jx{sUs(CYeCW+JVEn{j3VoH(nfk*Ox z25VN_8~hwWAp)XNfv_;5lJNsqjac`M6;fJ!q=&uB^zDJE3nF%4^YN4|jv-Zg1;c!C zjn}rCB74)?Mxmgxn_}|Dn}=nw#9G1sopJi-e}rBzr}AU@mRtPG($}z!5wM|LiHYdJ zsDLxZZ#BiAk@;AhvCNvnE4-+)lFJ|dgPNM|4RCpNv>>J4gRl|>6De?TENSk%mQfcK z`V17!fce_3y|=U^S8aC*wkd#V5h6iz>iCd}!DJ<8R?UB13lv#3pP<>ZjvL&hb+oduR*q4+zNqYi+eQM*tZ~ zf9-(BvUxVGyoQ9wyAD99%0CjLwbZREpWZEl63^+-n!|efLRWu4Z-Voev8hV$tyOEy z-ID*{;eRgQ`>eUD8i9cs3-G;I2#Pny0MJIl!|%b!|1aamz;5oRaR(Ebuvljf22!1< zYf<0hcE;@AfIZb{Rurj2r}>*6@ZM5kHR0v5&oAW3sRQgm0u^yhB-? zioQros6dC05&R)Cq8BfFgRgjJVM6VS8u^Ci8_W3qtr&h1QOIpK{={9n_rROK_orqC ze;{H%6Np<`P*YGh6fOr!T-V6PepW1^($1mgON~l#^>UJP1ET-tOCHB+-ZcotUC^&m z7>l}@Jd23@F!=U~i=z?k<@rsiRvK2t|B+E_!VkR^4@eY*Ow2l2&xy@6e6(V;4rC@` z*}3WhKx^luoqnMW7$acVS$GVyDF>T}1J-l*FRW|=V5Qd+Yarwl?2~vZ1Cr?$15I(+ zSiLr1HXpAeb-l0}gbY6U zaj6Df>Q<*BQhuKZwXA!a3PM&!nD-|bj>uf{hiwb}iMA5G1i%~e8ldtQhjt?*LZ0_~ zm)1}RbeL2;a3TN?)|jl=n_!@7)zp`wHBjFjP!)HFkfGPkb(ICRHE`+j* z_HV%;0nkToL*H%j)5Sv*UCzImf~H2sP|6rI_OBd1kxh{Y4q`#8w0^=1wWb%< z7KQH=;9}&>ht5QlA9V6)lBkjzznXq3>pRhmqT-* z5Sb9HACQp_3X@H&kiGyc*uUn-6&|Z$lqs3W^W05HTWj*D2B*wt5Bi;jKykZtE8ZW@ zY)U`B)hbFcvXn<#hZxbdIZ{1HDMvqZ7m5XWfHi;4IKk7F75Ly3qT`*A<8jk!PN*Uy zS@;YxkHTD&Ik)W)CycV~WY>mDAkS1PHQwxWsw-e@5P{Z8OOc!G5?13!WudsYJ`lv_*4e7<%*`B8-kI*)Pap0pEL78^#f{!sHb}tFK z_05NzL-w5mx4*ywn}!;}WXFD&6KY4e$7JQ z;6LJtj%WzGnmW!kX)&xb4w4>r!KY9W7rX=^Y8%=3@2Yrz69^U}iW);A7C`TCIeumr zHnsW|m#W^+4nyAG55n)R{sn2fOo$Wtfkw~-V$qSM^uSnRdS~x_H`D?QMG>z}g#-mAt>! z?-Xw20ANqp=D!04Qu{2e334uyLGKU9FoW5t`{JG>QPLjDXmsJJXQb|Hq(kvm=}c1#-WvZUN~qwpNA<$KRUr9GAX{gA!|mAoxLmu17Y?itn?&H-okN~ z+OV3iXhnH`(Sy_s9?#LPN3*qOK?4{?Ift6S1uboZt=urj5*BFluaj<{Qa!ct7#V*a zyuu;#7_uNEiiir(`}69=pjSJ%mjqb_ISNp}J65bI1IgEks5XfVh3zoHabwZ;7daCG zN5=o9DmnXW(3Y?-`KmRj%^&UHmo%L~idce54zV1^fx=|Q^cZ#YBn#V_iWr`;`%k9h za5n9WG1h<;c|%YCNGT2r$q_^i7Kz%L$9~gLJfB?;EUPY0WPz5TPN- zVxB9`QPmC845Djn%sh=K3eHwF#-Rgn9RiDXm21pH+LFba*%P-!4bSXY%Y4b(N>!(S{0-|t9Y`_`B=w8VLP@+MpWHP z6M0}A2|V2a6^+K4Rq(rVLW?&>#uMZ;F zn-MJi&K0v{im(xJ4{Sj3uj$2b7m)UD(tX~T-r3&&yE?5W0!=}Oa=RVw6ALVvwLYR0 zKX2uuchBWKLbLQ@1mc8L)TFfplKX1^($C62FRT`$>w`nc@rXspc%;oG>n1)29*&&)j0{8aJ0I-0|I)90F#7!B8{WhpG zO}H_>CY*0!M8lG`OSv>LJzvN&>_O7`q_^sk1QPbL<9`3g;cZrJZ-W(bK^+qg?o1Wf zDx#i5%T2kp>x>Ant`&^lmkpgMx;BS^3i9BSpc=gTHnI=JeO*!B*FC!iwmI<*k{^q* zb&D)N5Mnx_Y`>h&m{!cuye1x+8UODC2ZdYhmRD3q%Sx0&$sWfmk>tq`e7|9uJ)H6l z@pv(*i)GAh?|}?gy6Bd0sjszyapPgcRG-dzAS zK+3-<8AD7w2?bTm^Wi1gy6+%Yhw@ha+lEX8)9{k#+>anfX_8=Piv6PA>xKhTX2?W~ z?|NuYlK`(6!O&);iPfV=_Ze2%@~?SspA#Ub{wO+wO(Ar)yL56rRw_VO>`r_;O|}k= zvkM`r5y?u7kOa&Q_a9>$WmvLa{h3TsgR&CRZ+;J}>#55xxleB6PhIY8r;KF5_hUlbm<2W2G;`dEV8M-`LbWJ-+Yy?4CC6z|KFXi?> z(ZQob=s9kmxgU4=LW0e)7|5>MMw^fI5!-;wx)-{Z>tcNR-72@P2$j{iWC2H}O#5`eLprWnkhIE`{O9bfHgBJ_e&vHuK2LLL_i-B)uO*Nq9 z6n7p$ug>1jdNpKe?2ShN{kB7L@!_79zZ7!%#j;RS%5TkGB~lEoeLzcQXz`leoDIrq zT8PG)7-SEDo*>9)=kQeqw)&OdESjuoP#07Eeh}0aVuoF3jWaw{X_F_q4@o7qgTqrQuOn&m#~U}!c1z3VZjEgFExFl$o; z{PEWda03Q^zYDt8giR6mRiZTAwpuT_(`Z%!&(QSEm?c$ry^XUsf-9(fzILjt%u76k zO95AYz&i=mlNbYY%ygw?k~~tZUfSGTZ5{J+VBcacBx6{nW~QT}UZus4o#pm>EDjlV z8a=$r`1?5DmKLj|m(&lVE$KnHq*#s9+jSBGgKEFgZBHsFO_GhCx8rTnau_UP#`Y1| zpNV}~T+8LaO7&#t!MiU+B!Hdn9_qRC_nq47)i2tF(s8aLA@p0Bj1KD(Q`<4V>~G_y z=jLR`-~9B4Tni_|z%%ze@eC4dgD#r6bh-H^pWL!G+j5^4Nd=92PpbCp4 zdqF?4s=f1{8R7(fgTRMhBxUItyY`Sjn>sxAuL0MFsH+_KE};O)b6dVa!0EQ}>1UsN zGOJT8ch{et9hq=L%d-{McQa+p38&9)1_G_2^CoAI{L_NzT}d}wz!~SXU7F{{E;K`~ z2n0XgH2D@yhO<+l$@{`-7Jp<}o)A_E+dd>flZGD4yaQYU$HiViJM-)9^y`4$-tgfF zrpaBD3CBBrPy+aG1#^<3c>+aw)tR2mC2$eii;RMbQf{70Vcso!V5xkDl1psQepZ+h z!OWC{8q>^Zz7F%tg$U&vc_WXt`(fTs6gn_m=4pS}FghW|V#|J?V5wn>B?|iO50ccq z%8B70N9Yy}ixV*rEgzyOO_7j{YZ7k_3|(+jeT%9tn@qqKF~`$EnLc1s&Ck$U(|mT! zUYlFu0RoFNk(V~s5w!b#z}ZkYk1dCnTOkW~;(p8)rggy8N)bIGsTjxZDQzWKhhlzI zRTzfy74zqH?1MOrlbeGyDNOVVrwv0nJQ48tHxNQU_}^nxMx7&y;!LuFmAEDQCJDxG ze*uW^*CE~ebCP-nm|KO2#QdBDf?BnpGa>pm(fMU8{47bd7OD21-d`kcXA$@RBf4p= z`|CNVOSx`{B1YBI&YYR0Uy5+B`nmH?r&4r(37jpM-E{=*SnZ?BO$f?MMoc+7opRDH4wZao+n6I*M3+Es7%6Q#9<=rW5=*fC3^MVmjL%X7d5b>WPGZ>;jockolB5>7KNG+q9xJd;o<~Yuq z9{cE9`#IRv3z9=T4;eD(HQwAXUiyvw8U!~7%hl244~s!W`h(#_M?$_Lbf7?&V_H|> z&R|IMnO!^w)CVwH7IY4S;(G{(Vh&;#1l(Z`u(OCY2(l_bvCHY9@Y zw-$DAhfokPywxi+pr{)0dQiV8lO4-ZR7P{28%{)_g)l0-KhxqvfeLxr-Q;cVq^#CMo(z=?csEs$J*d zPsyKh@o%gA;RfXEqDT*&>LPWn+iWaz{*tc@@SNs`ItbV0{Z{943Ic@}F59(1Pa93D zKAv;k%!)6e}bF7-DL9vcy7haeF+qgd+Ww687$QUAVC)q54!i;1+G_kr&17|>uW zP!gdJZXg;?_HhskO?ye{wuGgSC)ATx!)*)`&>r{)E!6}tF{{64ZDEBJ)1`LP-qT8%n z*G>IAhkW1kp97|Qzhcl`yOI$<_On}z;I7Wjo^RVXdwEJTURVO}ZwlvZqWA8-r`#^?=brs#b%)&!D>ebWY!-8a&sAv`)K;BM*Q)5?gcfP0eL3PI^On_#6E84 zb0uomoMPKa1DFW)MW2iwsCkI>t>J=#Y}c#d6{Q^|E)m7kqn6Yb$MDi=*$9;?eOv}0 z>VcmIC$+=j4k;r8*C|H<))mqk+ZE6gk*8gjt?P?1K>;x~NaVpSi@!w6ze~R@ z^c8sRbfr|zBCXM!IksDMCAI3Lj}X>8V;yr<@YyVf|M&{eM+vIy@U1Tx>S2_d%YF*{!&axV zP`%~+GIAx)0}hYgWf=9H-myj6Zo!o&)KcJNjMRrGlgYTNaMAEW7;1y3*29D`HS6WrGs<+OAWYnD#4-T|m@lXYu#vjUDgRq|lq(s4j= zb_}dWsu2wVm-p^>gKShM4NW5{3`ziv(`CN}umQX}l*!c|e~ZQ{n_Cp2Y{KR9stHso zHK_nizc~|Od!w@1%A;AEVf`56k)pXF8L8{jL)so;V_x>CNxbAA`mNA#f=TgD%_*kGYIlf3RsUG2M*SXR*8=5>GzgQ z!t+v8aMm@$xQ}^$kQr+Ky8pMhUSX#(S@8D)p8*oraPlbrf^6nZm2{7jrg?2V6fWEz zDkHM-3jrG>*wKa2*sYU3}64Qgb6I<>j-64WQss>QMF83-^ zNdyvI3L^{XeF4@vdaAeAiv=T$#qX!*;u)BMV{XU6DqcIzNZg(%HkYOc6Sw@E00&n? zD~aI!o`6=;NJbU!bbbo@Qpyr2L9;psV31a2cO!b`od{|?%fG7wbTUT=1q+nn;v2y=M9VHn;23FqE*|ku*iDGc@G4Q4x54VC8S*fM>*v5 zU;q!tGDO6|`c%y)&e?*vbI7_s?`+l478)QnD(IUQ$p`W6LV+N#hMPc*5=&Auzkz-m zodbneZtX&RzSGAFo4+#!DQAbwTkk093=AHX>jNyi5juTzyTJC6$K0wH7aGLtw)!K- zMTy2#?8;4X{d!?e3fogIm1bb^d{y{!xdOF6JW~*Gij^=&k4v!I|V(KD=vG9iP%XAxJY!Pz3~<Me&MW7FM+kB|*kuIZ#C1wh&5> zO%VBzAb~RtdjdJDC7$2Rn#l;3rQ7${H|Y_Z)NhX|+g08w$@tv0KbaSLs*>{#S~J}L zP6h092@9Dm60nYJ`1a%W_;HiL@2N?&IuTQY+{7L9kU)^`7je`TXY^lj7bryYx|mm>qD=4H^+pESlxVXn+hT~Z@!lwu1-rq9PKj%YYF62=VZHl zENl07Y+PjMt0o=V_kxj8q4x9A42t+MBcyab!rGYv^K(*GWz z{dt2AKZ$lmIfgYQam)Xe9$P8fqJAK;HBLHwB|*P^IKkQSN+QxgR`2vpedDd`}mTg6{a8&*#iXrwyW3u@_I6+>I3AbNP48Tmk9= zpLN>U-veW3fFn%KW4Mh{(d8lKe_9_RQ+S8A{Q>X$Vx^*N$mNzxYd0hWcb&TYsZ+OP zt|_h)V%bLaL$u+U6Ze7M6y^{<19alu?-*skp*5&KGm05+J%dBo=QLCtWwPW#dprdt_tOCy#x(LY}i&^ z3awSDNIOl;UO>G?kPucrV9fP0te9Ma?aLkZRN1%)O7(p_Jj@NgTw3xb)!^Mq zWVwM*IuY8eUmOt{=aa4RdvT8}0L|w&M5zbq|Jb^%;%&W-9#sv6TNaz^fWk84;e+ z&*}IZltHQ?4Xj-?JlU`AoV2V!VUG#~jd_byPMY_|L#@S3#Q_FK*@x{y%N}@Y2r3KK83vtuGRQqVGjn)< z&6vz$iBV$VAHuzUWY(W_`AVL^a+z(K_P{dDn;L6!?`;H9QxZDeroXFoVS}Oegk!{0 zYQC_qINj%L7|Y`C;7UwBaBkV}2Tk#?qk5PhP>Agw{t(zh1^^}vkWB)GNn_y*GwC>} zA_>#ZB5J`+wzFb_heROyzTkY;*sbr`=~JeBe-b5D9*!7V{}~s*T;2!r_`U+H24T|MPRkUl<-^P0}8+24qm;|Qr0MIC{mEion<2i5mm z{En!nm6y6sofQur&vfe=qrH|^Hb+H5)xSY)i)4u_NqFyjHZihCisZkMQR+fDcu)a* z&_$(FO$;ZOfyv}@?37JlLKHeyOQW<>1cuHVfDxV)31uIAXQ32zSoA=R00MoRmh9dR zp#rLbgCX6}cLbaK0UDC5q)T@6Apj!*=}_@fE_5TFnz_2OHUZ5r3EipvzPh`dw-!%m z%zdjk&~JqE)l{tl;^Uav6zs3gwXu{|!DmClfk8-69;nD?s0y}oQ}sak}}*P(p7+S#~Q9t_V6GscwAlpdl5 zncF*bq_fJx0KTP+s3(nsK$##PvrcJH`lD0QZlflz<%lJx_6t}|>E#N8wO>?(Yb<=z z)Ldl4IKd>^ptb|}&)rs+@_(Zc_$ZHD_Le_2Q_4>o0uXI-tGz+DKE*Dbe$Ti`EzNdh zn!{nWTBrz<7&spmA4NGd>sESa)Lsrf)k7O20%+p* z$?=1M5iwUz`z=%*we1^gnkO9(&h_+(Y6wLj`p_5x6iqFx5lvy-j&O2^qO3uj<1u6E zSrk(IRP@i<7WZlRvRNOXLS?d5!2ulxfeq)bH5vpS@dvEw$Lvy5l&+R-yD_-~{fE<} z3R=vBIm}VuX{mQRn9JX|F%7JFLB#>@)$+7`zfjkHjeo^ZKMC;Th?@8bZY(wX1enQgT*;wjkg(Z=SaoIV42Tjhp6IlP zrUlOIHs!xs~tKEVYv9jyZ_N7h(q!|u4%uBswKx1{E{*~CtQWC|zfB#~#( zbHer0a6ri|q71Ek3!Gb?&Vr5AbwR1)yuYR8c6a(YI)>e-Dd~1m5CZip4`K%$5z3nZ zd2V22&X6^A5AZ~rB)G7UQ;P4SDSV-Dk(YUHL?^dvasiaBixoEhZ1m5r!|Mb)Z~Jot z(dy1#zu^hK@}Gif`PErd{=>Mx0_F|nP|fY2_GN*NgaA9dNK#YsBLS`bv3ztP+SJ0W zhPWnSZ|a@LaSzg0sEY2VX*5qth8~L0Uzp^krvfS4rA5RkZ9ARe`cB4~@Z!6nW_HE? zN(s!1Rv+{gi#B00P}f7-zjg#_z1WrR*%oOK6$x*oAs$W^Nis`iVQx{u6FHh&_;C*6 zaMz5O*h>#P6Mi>F*R(Z35LD_qfCz+(^lg2<7RYmmUcE;(L&y5?Se)#rE8ym7IJj4L zjHx8mm%PV*0m0xF$Dz!mLk8)}t9@7FdPyD2_TDnn@g-JN2U2zVzq{BIS3W5i^mQQ8ZdiO5?h>BRugfK1)*~IBNzQBrky^X6crg8EY>irW4<%#H*Q_}%QJPm&3 zkLel(onGByZA-YE84U_rG1Sn*%O}cJc<1lkdx5uBN@O&^lkL+uT|DLscLdFUfqZd> zhG-|5VR1T3J`+FVGnY*1Mh_lNI&xW^iMNDcK)r4KkJ_@(mxuBnr4I55+T|yx)du(Q z%kEHGm}Y|Ht(^K_9Ex81iHi#Uc<0;l*HLVR9OcX& z5C^25@=Vmwp$scAUYTVSpact^34254;S9yzOZ5gAqzt@x&u8az%3&1u6fCfeS)O2w zE5Nhg(N94AqkZ&I;R`RP{}V#phroAT3?mw+i3H?$(6ESI*PW_4=Q|(&(ft4f8A@md zw?SUprN?BD*e@0vTg8b+&h)IF5P;i%3>U62UlJq7AndiT%zX2`1+d}UK3ujeNGfyp zuYO`7+ZJM?J4x{?EYJVaonBnW(E`IORnN?9WzD!_VW}F#>9|agOGXtMN@CaF zV=1o0QNw@7X%dF(2&{Oz~1vW=~Dct(!0b^CP;$%E# z$PVd5OMl%LXbS4Cz7Z3$iRtno=FN)DzbF?%<@+~puGVZp7Sh91$> zP?!{D=uyH8lpLN)&Mz5 z7FlDu^8{a-cL*Gyh|yCq3Kf>80&d)LkLj<(4?%pUi`a#EjB4p?>c1e_mjU_YG5So6 zUpDs!ky7*;GT!ntKeiTq2SMaiV8Z4nb|s3+Eff7HzNZ9+t`7e2K$~NgiU1Gt z@tLWesIXD6AqyO;<+}9>>YpB+8Q{AW>7018WES?Ua+JRMS+VZ#4rvi8;N0PvvUHrG z0tn`|5m!G3#@keYUI^I9W6}uul{WI-Z3AnjUa5`o`XEwzB#1EX4Q<4*j#fjjNKzo? zAx`GvneSC~Ju>D-K(?^dDdKFr_62E}_a(fbfcdZlDG4Qy+9L=+?OX@_KzX^Ncrsi^ z>lZR^I9wCk_au{gZ-dn6i55CR;QKmh%eR9EM|3)_HP;6gxXfqb*v+R-vmC7^mLJO- z*+)$=jt)Guw5_Xwk`7%Gug|=@HVCdL3-${)MBUSpOomc0(?C&*u|oaPmqh1D&VC`IPo)UejVi z8^U#f+ZeMoUgmva@w09leO*~rG3xJQlgW>fFIXaH?B`#*4I)l9q#PMBiFUBs#wb-Gr>XeqF0f2&tzh^bQY0yKbn-FG}?ZRco-Hv9ac&1e0Zl{DQy!GPjM}|qNA>ojU;N}cpe`g5q zi~c>KiHZPy_6iC?2#N3Y*?Xs!)k}?RW^FIWUneqD9qy$t@6k{nG7I}Ch!xj*okqO% z*=Eet#uN(R_TStb2sXZ5dW51(HC2I`dOlwFQ@^3VTGkDIqiQg1P1KnEHl%oJ8ma`5 zt{Evl+Fdl_MgKPrC40nGd8*B|9h)z61;jy9quwX(dar5%$}b?te0<1JIPeD}l@*NU z!d}F+onF%3W$;!PRS~eE3I>oRbE4|1s`(uzXZ(6HThh-JtXQZF3nrzW|p(^gYT80CM1{ zn=VS!k<>kVljbinYoSzIJr%4ZfyZ-UB=6>&D$$sGNpz2cECG-*6z;?ZU5m@4L%yWD z@A#+Gl1MQa^B~Up2l04nHwP+D?3)tsCdq2{5qL$2R0C~>!yWQ?NDIP%NtB5G`-+1? zE$MU{ViZC+)PPu0nuktEmG393=r4LOk0Y(%E)EFRc*h@mp&yO|s_b||HXy~D_i){Q zpn-~p_oDt_wFjsvv4-`s`eAKFC{mAuXaJV_{c&Q?lTVk&#)PRgGgSDN5@rW+AH0{? z!`!qs3zSZ8yY~zuxmvQ&(%P5x0uHK;CZD(-VM<>`XuAU42;C;D#769N`7Oq(R#BE) z0AX9M*twn+jn*-&M2nigF$Ved(>|9gkZF!2zGyoC9^TUv8b}oJIHWrtyf9LjvbqKawGGi{_;tlo}Nb zRZ_0Nb%B^Mg$Fw8SXm1D-NbDt1e#^aT)gX15I`F?@QsupB9B3Pb*;m_AXk~ff9alk zS?)hv5fc zNC=qn93MhUABjKgp!aOQR3a5-at}|Dt0KCGujvQN!`-^p=1w)0Zj}tFf7M$Q1q?(v z=xPt|s5R#+n*43Z@<9!r!qMH3kdvzQMa=?@GWSYMYz&nzD^`7ALnIm<8U>Ttzk?w) zx`$rcCwtfceLywFEI5@JHzIJ3a+M9058%*@TqKsHz}5JbA?I%IG7tKZXR>8F2iY@q zw+o?5L_db=YCYG=@6cw^C0E6Lr8W1W%}o(QNd@<~8L!vP-vgTtvfF{h!8K|qslKgf zs&4;QF2SpT$nX){+|YJS5iAYS&zBF50M`$J|C}+fkqD4^R5I9UPl1!#5We&#bU8Td z^y>=1)#!oMz6(`&CtNl|Z6kTr5wgu#fM*#NaRJ*e*);15W$!qS$$kR3SVe@kycVx= zMuCS!$h~mea&wtF+8^kXGUWAcP||)pApnIoJ`sh9#P0T_o+kTCzi#*n^GS3%7JdMi zo&YHdu{`BqJ?g;qYnh4T#U?H5#s(`A!sU~1h2{-8*&{13{CZExJ9cD5wmwpV6%fOdY z9)g^w_>b;}1_{tKhfwtPuJxJo%O_Ocs4fDbY5)BmVsX5i5L9np@{yxaUTN7*DkS=H z+vR_kuMk4-{2x>p7!x%p+-XpHJCnx!lRZd{21Gqc%Fse<-)IPnhO)AMla&Uu_-eJy ziWnmneeM89W^cR%Kal3sex9Mqq)}8(+W8Ecr0#ijI;uvvPwWkGv;IG9)RIMVUk>0v z1$fzWEhD;#V7?=d?ku$t6%q?n*I+R>y8Dz@S93G+bfma%YYYO>U;JO8@B#`86u6c{ zfT_keq{MoeH+Jg^IA8w(7J;IHpBJU^yS$9e{}DJI*x_C&fB7Ding@;nX2z9c-(ZRA z)K6LBd=DM8$}^OFD#Hd|={)pQPovrAaVvB>vrO!qQ{|ZR<{8&Djt};MG=@onA%&&h zIGHp3SDz|z#$;Qb6VL!ry8z=D&A@nrHKWk%n$@S1g%RBpSKpWnHZ{nHyOeKW111Z~ zO-2EBL0W9~3!LROEAz}%(f0%A+g$q=mHUfsy$O6jOv&Tnmy!1ZFDI(>TMP9YDScZK z?6HsjLIv~Qk$m}l>*bF6(`*Sl{=@~0TNRtdK!qw-9cqK0E`n7acu6~q&o-VdFf4;y zEEOzMZSejB1&TT9azPM`op#PH{1O1LQ9c(!VAGOaKpZYpqL}(16f&c~+1*fi2(~DQ z={$aUspjc$(y>w9b($y5Ll?8qZ53cjzDC0~787`S9#U2%6_8aYc6i=Y*x9q1XZTVM z2orEywPsW4WS8}=Ob!{sX_)TWvYCJ^;GzT2_rbh8q?`ZeW>L)3(fm&IuO;jYP_7BJ z0&On0j;ITqTp9B3HHb*CaGmak=ga%yu7cv?8Y!c5;kn(u2*^+X}_cWV_(6DY)Nn7n5QO0!2+&WH?m!@ zMir-X>36}ZF)Du4k*1+j% z00iJ4^?NExxPu*)+2=titv50jEO&44JZPC1k9*$RPYU;E3>Fg2AHzxpXWL|cnse`V zX-CmjV;@CPZVGli=fJ}mzd*~byv_V|jtK`K2g~BDl zzGBzITDAuMRoeyvH1knWR2kVI?|62y$_BIl@JiNk(HGHKQV*zoo~B$1Aqh^f(DYQn z@nhL;$78&{A4>U#&WwSG3|8+#X$U>=2C-Mc`!!Hn5daU2X7VLvWUaNrY#b3t>{OZ` zh-%sgm?KN~F1{Z(D62{PW(xKBweEqJLlYYUe3NCTZ84o-6CzR1Q{V5{y_sY7C7F9E zO98*aZ=XQ@Z-@^UKM_Z8=3b--%YpN2!@|z7EH7(`$Z_p+8y|L zZWounD_MpKwbS9G!f}X}Iv`iZe-{L&pZOBiG~VYi%ub(wzx=_yMyAgUDvR+C;S5v8 z%>wG$XTN_a|9NLy5ClDted>H;9;7c05177092B0$-`R+5egYi4S>qZmy&x$It=fx=;57$J{lvy z1i%#X!nWkHcWIZ3BrK%d9K=v?6SZZ`Kbg`#3%Htj+fo~6{{Xy`U`;C-99#&Ssot#= zCbo#Js6*IlyHETm1@pGo`*K9+IO7j3P0H_ZFiH%um=t=`yTkG%5L^w$<_u_;JzyVt zAyL(f!+UGr?yNUocXB75)Y{wjjue04a+FXolMbVsxDH;JG42+Qnby{RvGCI7O?;s5 zP(_*FO|ti=vj=q2jq<z85-T2>DF5P6RI8;n|YU&FN+ce zrq2S+hw8z~^@OgHD=3CCT#DrAlL{!!fB0FN3RIsW9KS3ksit7BRz8~hJ#;mXL#JLJ z{9mhiID4-@4R_JbH4}e}@C4*o099M%KzQV0RM?)pHZY~g?ULmS{)&1*KH+m1!l%}U z8j=~QVP;tb$E6;mrwGrBW+bdbb~-prV+jTAXz3imC0|Om4wnfquZmN5`+|~gO0X@m z_rc_s*Q*eTS{b-#7)|Np74M~;guX@mN&@3U*vIJlgTz*`LywM$Y9hGjL4j78fQW~pbf|bkia-Z*4)(g#FMt%p@!_1dxKqDfCh6aRjg2ujZPZB4} z$CbZCFX}zeSi}I>eD>57sCG+y-cqEBM0q0u5l$ zVwXxR2~JXa%;$7+HEO*qwJxt8%ebpdC=bUt_#)Y%-(b`prU#w3M|m|_NNdRHhf!Rx zvb<$hKb%kW9^_So_7t9)`9*yKlp^4bc8zMu6bRpS6$7h+105z>Dc+OHb}FhZnnehi zIujgoz7R!-yYQK9E$>g@1dYE%kyNTnvrW7k$`*{qu(_6DR%z~pV!<$V9WolSnx4A) z@tH=$&a8VqiBG9xeuZRZtV`GxF)3EucoVu3f;5M1ndseV_%g%|{H0JBUswJJ36hjQ zjEfH@603lA9lv7GW+ih9cB2+)_5+aMRZwLZ#Q@6gV^-xUl&81+WMWL z(4=}~Q{IKRt2!DN)SzB3{SGs8gs4snTi#Fz5|voD2pqX9y;G*|djkn-?|^=LViAXr zfCqee!7P@%&yNWCRS5DK7`^tXCSUth_di%M&7Npd6Q^`hZ5PcqBMYtoK_8#EPxc&| zs41my$~8#Sj2YDH0YRJ@%!a>w2|gSl z6bKVvpi5G`JxqJNGSj!`Ke-J8;I4;PYN8RLG(C;{3@~so*|u$utjIye+fOoDP{w%s6yQ-G3I&W zLRVEQnvw9WisznK#=-B zheSqT$Y~wF9|g|FWp!DxkfC0ZAO@K2ao5o}v_gqAo?Xm*O-kep&9Jf$2d4%K-U>Ay zIvBv)N&c6w6OhZhE`<0iHw1s3>rObCauMqd?ehsK?!Zd;(P%L=JnUv3RP=mNDmakY z_i)6^{vf6Jv~x?=@fX0+a8NGK&=s8085B=l?rXiCL37%2lXIeWq;WBAei?_!0_R}y zc4D^H^>t#hwi4QBIPRF&(BIOO;!4uqW)J-$|00YO%jX65$4JV*+4b_`uNNh-WF+I5 zv8iu82NfM1+)4%Vb)*R+ok$#rGyLjmTxvRFpb#N+Wl1~q*B15H(oy&0KJZk^&sRGl|&kc8FEN;zHcB?uffW5Zy|nyQUO9V;)|E-LF~8D ztLYbe3!24a`a)stCC0chWI>5@rUI)NS)8X8O(nJ;Y4=qO%L?T2YopDr;v1$`MlQ_k zA61&F*Syaz>PN&i_*`SaU<{Fob5HxpBQ6vzYx~e@kr6J7vISE&_^;Ivxh!W~`bDpV zGn)a_m9rB~I8HW?N?Lmvbi}X5JJ?7uV~cRL0ubw87*eQND4Gc43={WL^Cx7MXJsJ* zstB87l0@*a~{1TT#+BkO-;F$c8Arxj=XP>5dajb8`-7;=gxVJ_0yo7w{nV@Eux zqpoTiw9e^LQn^lEx^fBgbJM~$`Vq#nRw!Bkw_dQR039wSDN&bzi4B@ew zdJ2rx@QwU@_JNuGk;2St5D9U~pWOT=M$gKmRW3iA#6Rq4J$IJ-tqK0PW}AWmHSw~R zRg+V68iK`~n)cat>;}xofG0?^aDMh8GMAF={kd_)9Qgi87hPYq+7hy~b4)B8I(!7y zrI&h@$(=Tuu@Sp3Ty4BvgGp+sXNkvju9spjF3(bF49Ze@yx+3r2YG`h4SAH24gVX! zpI`$6-VwMnNJ`Jo(4e;ov~|f`b$Y}Hh(R*KW-7xdHW&WI4^DAyth}wpNtFslt5dc7R~I4e+Wc_Cw51rBD^P**|G7yyR*-=^EZI|Dgs=aJT1tw!&h7o@B!!*n(4;g zg}wg%(4kgxWU2D=X0Dnjf(!eSeS#I>N^4+2CNcbARG;e#vU!rvXuu=GkCkYZb&%`}NAp&+ zI7M>07-*+V`vt!#7*XZsW(I$q#oIGEpauWhx-Tg25Ot*sW8@0>g%3GOpdX>LJC8$z z$x~guf2HB3flteFpbNv*_f(U|V^06ZtLD_N*gw}(aPRs$ci9uC4N58m+|$zO5T6y1 z%KXv7kfZ!T%?%^~NKCMBH@HT62$?^cD$&xCY`@EQ)MV-tUiLyN>y1TH;JP3n^`&h9 z5gaD^bOV^IFrem9sydJ5J=j2m&e^>$r>Xejwpa?;qQO6?IVy@hyD8I4W>~T4;I{Im zjTH;N&R++``b*xX9aON{8^^+l^3Ay^z`#f^BfyAZCip!8C+hkcX&`O<4mu>a#1E2A zkuf&ZD4gwRTQO^IUB(m#ru_ithuj?D_(Q$^X`0bBx*4qvA78eQcy7nL1rQV`h46ux zgutwP?tK=I;D0iE+seM*ql9gbYBkYR5*g?d`@)~2F$?C2{ zQQ73)=j4-{{NN8QCol$~4yF60;!`0LpjNVyCuo(7-WVAfx0pcTmCRqCY;vj&tMZK8 z2}QsiVBFS1S*Q>B4YbVHReW(HW-&IjCjz1v+6hSms2)mn$7nq#r{HHPyrttpqDZg& zFDSrJEP^2QbGuf3r?1Dt5Ge+U8m^QRz=JwLFZ#E8?Oh{8u0c_raZj zGn$ZYyZ{7V$c%zUY7R$H=Fw7lZpZoe?)vIPo&oMw)YAdHi1TqynVhOo5w-&fVnZ1L*g5IHhB$ z2Im%_{|riJOUWE!gmqHCjf6>Bph*y#8Ut<2Eg#`9Y;XxGvM3InxgXv^S_8@>%5*8E z4h<{@PPOqU2{PeQR(S>C;;s`9DDP|XoTtsP2J!^$zA4mNhzhLURlDfVrwRN#tO`Du zYqRsp6Ycz}ytT2prg~Fsti&U@vDwZ+ws+ON_Xk+AYoC{A&|w$L^rFk?u?zB5dz(7| z1UPk|5sYq3=Up*~kv*g()e-;h!7C|o=SZLhWhx+!0pD1%Iod_*#|A$CW{kAf>J7@^Y@2O1n!CLm<;)AY zD;E%e>J8g}j5dH0!5%KMDhXp-e}VVKnb4sqXrAiB)D)eapxwm`H7gbUnb#h;db}Xz zTgCjU$ZIXviV#eDz{!c$sO2Z^Zl%ney7tX$>J<+(EI&Gdt)cz!<&$U>x0bZM-6ZfT zD`Uwtx+j(eL|>-ddHs*VW(&paS{n_VwgB^czQ!ck$$^|vQ-#3G9kQt|@cg^` z<72ya%NDVprh4kLzyKc=mp{nKgunB5?ny{>^l)OKMdO>~(cdF|^Mg}-1zPEm*t>-Y zK>)nEFjdq!SbD7#car=^lLR&Fu~Zg3k2s$N6?SIM@@r!a_)QC$w{Cji;o^C&{2BO@ z8D9N!pKE}NV}+a=8oe$ciCbP@A0U`_`ZSlJQCIj7JBv5>|Fu&*S6M07Sr4696z|Z< z>kwzy$+M3mBmMHaN0)-6n0x~B&6dC`=ADt;Vit=RxV$=g#Rta&bs6dYu4Zf)Z!P@A zMD|4gu!>(wX_AqCERpO^kP~P=2K0|l{MQC2R`31_fV>-+As1m#NEDX<`wdU4Tl>SW z@x<^D+sS@yLpgQ*2_+cr>I!(7c_9m*Y85t!v7}lM1po)90f}y|jpa7{$R|ehkMmY; zb~835u!r_!EK)gK9e4E}gT9c;{V|a*{ma(E=nqJ*<(i=2UU5ZO@<^C9{0%3mLI8%A z*6Qth6fxtI+COJp7PQ*NxIe0{NRS_%gcUo+AnLU-omAGIGo?8heB^zIBsYppc_(FpK9TS{>RGoqpNdCJ)MPO{Y$f%o2uejKZQC0*Yiv_X^@nSg$nrSH*r7{s*P}G+E?SlWHpdUP*bV%l8;DD-D zE3BnN2#ds?u597$Q4Qj_Roy@BN+9-JuQxNVDNlNX#N(Gfp{V7=E)cuMlGf;kE`DjO z3-Fzk{hE8?yb=s@JT|ka&Z*bSeDVqOerqvThZG&F5w+SszSqjVDiG^#I#2IaWtBTK~+t7 z#1!eS@yUF)j7=+?4;rNDDEzwJFPkB3{>J#q`U~n$WutMOdo_=#?H$-FiWysm2aln# zm!_W}w91d6{QcKy0IYwp=M1}_R-1Ozh(c^S>_#&X%i^YyB9-xi0TQjW?{>$ye2bH$ zayglHe{1VnI}`_1VV?#(S!pW%(`6m4SZx(w#Sat+Eq34}7!zA+zJ4Q!mRIr7jra;+ z11{>ulz!R0&^<@4N430pF86jOC@X_cn)5<<4Q*;gyA#$qZOD;PYvC2aTMA*QKKi$* zf>`sVGL`TiaS(WNp40OsupanmqK7c?-zU@VB@VQj?>G0an3oRBMsD^q_gVn^>A3l| zP{jEm7J0r&ErkE8jSl1@+o>JRg*K;>M+Qa=m(9^f;kt1-_4#Gc?_7FIhH-a40De9a zh+I3Xa+{o09k1lV|2rUnL}D8w<`(z+iPENbIweST0DtSBT6D5SN7IKi$(1=boe1Wv4+{-jvwXsNe1JD}+5ghO z{2ydFG3KPf5du$^_ZDuCmmgNY)z#dpoyFQlnrhCui3!BhuWyMApqtk>y_EdulEvPf z!_B1$3DN=X@edQ&UUfqhO)d>w;wd93vyua!>BEo-+Gy>pG$-x-sbBRoP7t$aAOtp-LaZZh#zVD ziCCc_yY_q*CXws1LkzaMmO+P?N7(@PuFL6PxANz#>)rev-4z6ihjS)L4GTISk5wE8 zdtw@+3Q)y=-wAv`vgeOP_|;%o&;j}IZmYO%vAQ0;*OC|5e#RxR;foiIkwKlsj^|SI zcx^_{HXN-F@C6Wc76?1BNA!%#HDSe|m}A%3&)o$OeV2K9q(H~9#GSg@cdj9s_-B|B zmd>nHf)m;;Z=Kn>Lk1xI4q}-f1@l1yBUlF`1FL4&%Wkf-cMAe{c*s+?xBml^VA25n zJnYUamnrZvt8l()P456OGu7r8Vz5Q{j_h5Sy z!3~YroFed=gaKnAegdxC2oDX8OY&r$EFxVbfYc4M9WdTY(zkhP;(=>%Fs@iiwea-f zNBegd!KCs9i}-2T2yq4&TOn=Dq}V>YU8r~%!iu|dscFc`D!_=5YoeiJ^V&zX744GH z5aT$9K8wKRBrH81hBJemV=AvX{9sBktx>FJx>2@O6$lLBh4- z;#_*gU@;RX3`3?WZ*MlMtIC1hsFTu`RO(Epl&6IM3(C^B&&!m$#w^i`WQ3Tfg27vk zLHZIhUWvmsLO~-ObX-377U`BU!nh0F;&r1sE>7G^n&sj`02!7G5+4zQt{LyI*uhmIH*1Em`atO$KCbL{OVpHeW%mQ-Qz?o8l)em z@|uAAV$2Y+?cWWat?{*Ku=_7nQ*Q?%!?EePYwmB&$EK!*dl98&MRd#9LE^$-;LLp$ z76mH(R;w~6_CyBm~)ZM=5Ni%0u82go_f zBRQEAO1RbD6~_|FO`~_&zdkw9XCSsQfJu8olNC+i!+)ZZ35eSn`=X|4OO%9X}6mL$WDjhWM0t#JKSrUVJe( z0Zsv6Hup|Z5)N#JPqEx&Ub;OVv9seCB?A8N0qe!Cls-{FP}Tw!E^^N_0K=j}e7^|* zR@Y-raFa8XJI?Y}4fgD`+J$boI>O_AwtwWWkc@oh-G4#9c#FgEx{Yh5-rol5od~F? zwID7DiFV3T`Shu`3umv$?j~)65~9~1Le3~{TCIW0^rLM7otLg08rcOS?@;V9u(1u$ z8_)Th+p8?bNZ{t7K@iLB%64$vV@`oPNb`qMx>ygk7wpx1h(-$5&@zCUgtsQbc*iSC zZKt6Owc*xHVM%CSk8d&Dpl|w*W4(IP3g`%1W+D2&^eYfN1Qr$4hJo{Q>B=wbtv!ia z2r?5i6sLW{DQ^AFQeEljM%}j@%e%r2VQK5{15O2HyM;*neM|U8FpmB4Mx5>Z8v%{C zL?00RX8t7-I1^k8X!`}inPkoPnv{#22v`;s?f+U?Rb^PscyIubpU=@XdA2nri-+JU zPpXo1BQ%5z1pr`JA$f@d21$URf4T>3?!A1t_{@q*CK1{}(1Z6JD4AtoxG1=-<6a`h?89N?}>l z<=9UM`Vc#tsXPfSqgDhTAz7mZxp2|}s@!}f0z=(XRW+zWrfmKuIV6*sxy4zLnMN@b z7z!(}n;!~{#i|IY&S9j8EqiJ9pJ$wxy!&MYDHHw=ont;@<=W&J5iv>$w=-=o#uh1& zh({*a$#NCQVs%dI3fQVuD}fqfbvcLd>_ha;G5l3Alq0$Ml5GmOzm6gcMEXWJK1MEW z%a#bT*h*;o;nYK>X9J!g*X(S_z0~h0HOlRo#PHa*vMgR*@Be zd+R*=PN=?K4}v;T;YXg)>i_6A1C?jqE|Gdd_dG`_&p!xzPac;55ic7IdEOICN1ryR zL}TFr9xDWkN=kW_W-ziKFX(?~UsYE?C2iqnnxp)R|y#EwKy{ql4t|qvp?q zIzIACI&E=65sHVlbs%v0uVlH`7btw_FfdzX`TZjaNJUZ!-?M0$9U(OKLj0MtiWOqryf2L?iB@iW=#^;ENlwkmd8$ZKOq;h8$GG;vICbs zIVZeo9#MY4QjyLByQokakeDZ<1ZxnU+tcAi{2Y?yOYpgAzQF5m&=ud61)Nj`go*TxCvWFt-isxDA)VQVDWqTyw{1YRh{`(oR!^M z*KbqxhP2L7z5jGAM`8@fV+MV}@DLT}z~Py5MNz%lQ*X*jFK>Hil5M^g9+q zn&xTxf*;Ca-ia5d07L|hUDI6^yQF1&ZJfESkhl_rxy31eY6Bh(QU6}@n$1bmSfm&% zTholfO(6<(+(%wXmxR_xQuOu-ULn!{m)Z};vn=g*RtsJ?Ks6=*Y4ijHO*FaR1`_Y%0&NHtDIDsvE+ck(MxqLvGPxg|(|KAlw z?Yki+Rc<8UJ7&aP*|&rX&N}FHu|>+kLdepTO!Je_yBX7`ism<$6%IR*hv?MJK}VxG;jyAPPf%hh!L^DW!UpX z7OyQL1kUCMukR=dL)(Lu?dhf8?5`AO#NEL$XKETl>rYtPC%Y|2dfQrbJj~w?XhHzm$Y&PINcoCUB6bp>JURZb3U@ZAR^;0j&ODwQj9e@seU|YD zTsS=lxWn+-9Zjx>CYl#`Fuilr7#yhX%ioqTyG)^)O48Df$LLw=)xxwA`!6yb` zGs%5dYn;S9DZ90rfKJ)GeNQcb*NVi8L~!vIYksKdsk{|!Mx5U$-Oub9SxOfe5&!Dz zi+N|kg9Az$%jUXj{HimT_mh%^+!)d>Was?AVwW82!t(j3@GA!+S4J6tP>3NQ@s z5A2Q~mo99}4i4_g00752+U%32l7SYUl?~C))$x-W1b^OyEA;c}vs;Jcych(1zO*Kp zWLco_q9DiRv_5k0^1M4?Fv%C)XL)z&ZzU&MvFw@%=}4l&5&yWhGj+t6`B@;I7ZTj) zF77bi5LA=L!+fTva5E2u+x53EZqMA}aFKFha5Q?)R`M0<-d^2Ne}hx-tM& zPXbXyb@5jH4fY0KDFZH#tP97r49Z_s+pFxLbMN^3g0>6<*QSz}3fR2OYYjw=WO-P%@%mR-ez#xb-EV3;QLIBP;}_f42Meh$VjjJYBXWyZk|$X04$~_N z+;JAvETggtxHBB{gU`k>Z*y~AnR-+TFv^lVxvDGXBJ+~G`*MsbcJY@K0XuZFM;ir` zX)J87yjVV0a1&v#piJT?Yb4(Z{TA0OT+{14QF4vhK~pwGOO66^5$pv$im}#xc7Ovo1EO>^ zl7P;}IlC0at8pnbikC2ecuA#2%6n*$k4|1KbRG_XZXrs>5U$TL?<^xD#p#}0FGtlc7p6Wf!?|@Svp`=XSUtIBru^`%ng?U+Lsrmz095b!&6?UL(ojI2=34`RKL+ z`Z4q5MtI14n!v8Koh|nEdFW7iyhIMI90|Se_Ph0*2*ElyQd?ZBF~OwfWgYt6==)pD z3d5N~=seC8Den#AISSLLv-}QV6|lmWQZ(_AG61a#0JvJYxdKG{9e(}9F|supy;F+p zfXt_-WMM#NAig@O-=+aedE}qZ6pQeUvWT=LzeIiZ3#y#iq7f zmK7U;zfBe~Mw3vV51u8#LrXq3kdq)SH=kb728V}ljD-pkPMHsu=ZCdVEzg<0nAe|R zojwo@$qnEhyS752=IU(?J^t6LY@$>dU7K6I&mXN7`?vhOmuHmE^uuE(l6v+sP$~|< z5%*-gq#T70{PZc>%NgTxW)?WLKeE^;))JrP04AnOATMc7$s^6FtnCfla)$;g8L5gR zM6zqEzl(<~W?I}5if8N5j?IouI{b0cFG0^8CDfqzbxsQg&!6Be(OUzzTC0MAfj}qz z@vN&Hf*AumKl>WPn=O5$FlY!6JG;3z=aRf6gNgZn;6_vLLEr~^NB-=(OPF%xEi2zkL^x;JSe}f~fudFPLcNS%E^RO7 zW2zAZ9b#)4W2gi3bVst1FqGFVo`(7e5Lu;`0vkV)g907gTFN19If??0}Rply%`qKXxT|1y;wn!?`;{>HQ# zH(m%hO|Hfo{?4AMwDf3YWZ}-qeW%Te{baim48cpJEmBg)Wq{5`#YKWbUjnbUI4uLd z4@T~?4ry>FlV%k_$I_kfw0}r$<@*a?bI6`6^fkkl2GU+n*lj8>wPKRxPmTe5Tzu*e z({NR^Lq{~w!{Y5(;ZzT#A!)Q;qz_YM`+j40Hj4YS&{AS$YX$;7N$>{)Skc|9;pf!j zSfGO~}W(4H$j(C8Yppk8b?^uFvt9$|TAAZ|z&rQe;kq6LyyLgL+@? zRhQJazHm0|4R|X4wRjl#py&c|eDz~6Xv-o-gB6J8S@-KPOnl>mrG7wgulX(z#4}m^ zBw)K`VS+K~rAWM&<}DQzoQP;K!2(ifO6_wiW3d6A!>r=IV?xM<`}2awI*6d*J| z+Jhm`O=Qe|sg3?P53UrJ0J#vJh4$uet4yP zALSANgPPPrS8tA{S#eBI`ne{eE5`%3qN6#Um6fU$DNXi$Jx1DcwY+D za~zPA0U=0am^rbanezeCD3f+UDBSc)n7Q9 z<{G+NmLBXOR&g#aH|2kX%zE7EMWNsZ!VOv%t|85`{bmkqS=KjKBlcg-VdDQ?xhfYG z#W<7{wRC;N0E$OXKdc+ZRQiQa*v1I!SW}+DoGuLY%mQFtdXOiIl8|HNF*1!dT5Bud z+i(&E`<${=Z)mH0neT%5vX1}Po%ETFOqvv`!IY^tqOhG4Pd9fot6H}4!p_fgw<3m0 z^@GD^wON<<$8`9u10jkYEp8;qRzDT|2k7JV9JBAnUH zoP_kX14Et!9_;|b4?h5t`g{qBwg@R9cchVNcKqPM)X}rfIW$Jyjvje9z)G>Qlh{Y3 zlQ6zN-mPSI5JxNYsr(eYod73dnJXr&_%BCx(A6#8K|HR z)hEmOs`=5knK~1Uz?Wt&+_)Po8cgfXCQJ6%EvcgU)s78cAraiJ$LIKRvw%xAQRwGO z+BygXjU5+e;7Vl@(6?=Lmwdhf+T=f^H6!g3l9tkMqvQd!C7LR8_bDKGodG_)o>7wF zbKeTxMINa0vv`Ojl+`JZXLb&k_dt#2wE%iuCIucm(7g~t?XdTiJ4owg^FzwT2Q zVp?$B>kFE17F-kt;3rziqGkX>tJ$|elor#aCG~wvxgs{OL2`77r}+`dqP%Y^4?Ku0jqF@}UcmrpyU?as#5VeuB2vuyjok)T zKk#^tKUmcSWTL*jQi9MX7bwR8Pa)^jqzMV)-Uba}xHMWhcK``p|JoL5eh%#yg}63TK|V`Z|v@tqS)4>eE70?GB>bO?MV1 zSGxG zm(B>)*wDwzokGcjgsx?3Bn}N1T6PgMRzJiHC$Rig5iW3zhr{E%ByTN`SZb4_R29K+ zSX@w*IzOf_Fq4AZ02B+Xbq{#-d|Fp?px@|LX$_J=vJ`iil*0+jKU5NJ?+i~r)s&d% z#IGGesn2?xEl?DDi=xO)XI{{$m?9F&=34!S|myH?awQk>UQkg9cWivfu}&kg%CCsQDPf9(U3%ffF@lF%vj+FDNW-9Y)aHmJYFd zvBn6!D+Zg2-iwFAVh{?v5h1SFr)WpCf0t~Yu+)|ODM55%0wqhk%e^hPtU$C=a$pHA z-(f}+N=3b_fil2YWr19d&BjfeYUXGa2OI6j)qD0RxrF*)40h`R)AcfX=l=sS8~`~a z2KCkPx~C6n7w^S-9;D(u)~zQEj(&9!0jY{>23tG`br5vyBJfPN%oP_=DPCQ!m@CO* zl@!YSu?y4rQ zly`~hc7{sV3y>NB5G=dfLGJPO5=wf^lz=vU@PpfqArNklF&MX$TFeXZlRDvKti-p` z9JXk39i|qhQkf2v#~v#MFFao-q~?6Ndm(dc!#S?`4l6h+al7yuzFVX1db|$T&m?-ZK4gpHyvYI zSwAOcOoxIhu2^szc!s%1rVrw>&>vAPSfm)2rnI=!h%o&HSo7gEF~0{oOlFjvH}=~S zc5kWzuc;r@6+_L2|MHOa+|NcAa!(8|K*Oaf7Wd{TI~|13a`hP$B4qwY2f6FIT$S@$ zq_{g%QYsN`Qm)?IoB4nS#hA8Rd%ypf!=OQvs+RzIqks-Exob2U*}Sc1&7uqR> ztR8mt2D1LRomZG8t)1DUw#k9|4%3 zP<-2hE4mn{i9lRWw-)BRHCUkkGadzFb~qyL&;lk^FJZ(KLKfI_<`YZV7ru(7SfnlY z(se!9A@xt1%=QLh#g*b5Rec$S{CicW1xedMxdml?>^SFnTB_{iQ0%q{p_ETA?~e!; z>Wq*8KJ8iK$HDA*-pdIWJ7v>_=);x_m^*6xC_h#?+IfeUuPJj~0JtLrN!qCJ5%LKF zuG^!QYzw_UB25@7r`8@=4X?bZ@lxE@*N}gP-QOguSID@}y**3v|nQQe);kLZ^sr`1JdR=3+?%*#7{MgyfKE#gcg;$|qpWSGyD*?98ms zejOrAm?9d7(dV&L#!RQLXeYpm?fJY_d@{EZ;w&fzH3A`)=@n?0zD*Sjk~8qIzFwws z5Xl@*&zx?`p4j{byRve9q9}E^tzt1=Na+<{Cg9r0O9QkeeuxeE-2bBdzR{qb@<}K< zh|x1NQ!H9yz2N{il;0#01;l1ln`Z_kjn zex_6DxR)EuB2=xg{%h94*_dGlGY81MQz0# zWv9KL#;%zP1a@LWqIRdBvq2;h&j|Osfov1%C3!rWt#H1O>$@@LdBDe1bN1$IDo4U> z?fuNyN?hv%T3Mh>5>Sd!M`j;kiWvm0Q59rOZAVDV{D2@25IjG&mC_AfB05&G@y&J; zZWM=y7!Vut*UfDWyNs8C&*T*z%AFZa_w_+Fb1{Nb2 z@{{Bo8d2@J(jeh3voEB8Qc9a&&!}vY!Y!~17bUc zHRlQ7=B(|lwSlnDhSn}t0)+Ko0r>}0Z)Cpa>aGQm3MO}7e|#uDNSXuE7VFc3K1~hk zNLYv0l%2di<@UFyXHgzaYz9|B%(pJ}Y!?k_ig`lt+R5L7+0atP;>+ ziuf4Td{?}CqU9hf%*I2^&0e26<7_X9hh(x;>->2UVF3aHaWu;pZ3=93ar*N`;-Vwp z4iuiHdI~brHdb4qHHb3p-<^zh(BMGGcbq|h+#Ul=dk^Z45tCk9G^5>K9bYC&o|q0J z?Wt)_LDoKItDg#HOL*{l=-@p_graoCfm{X8KhlGRN|X^Ru9s3&cG+jb1JYE1Mrtcp zHTl}*dRQI_{6%JY$yTkx_c0U_X#W|_($NGeWP z3gm#0&xw6HT>230J7m7F-h|PgWw=D$H?HIjt{?S+zxIF{#LP8Ick=kKJV0*MCFVP! z=ZgH(!hi^ZhIM5kr(IDjvn+*9ElgpPx~SDSu2k0EQ%!2h3DpxxXT%I#7E-Dc`f3Wu zMY9%f;SD;E@TQ#DLoIVpu3h6NoEHe&0GQqYmk_;$Sc&Y)_s?PTF2`n8%rkQMg?S8; zEFFzgv9*nWQ>>2Avk&=sL!;}+uQ6CP{WzVZ!5w5QECnxqW3M0 z(ND&P4v0T)>25$OLC6s+dByLvTeObxRTzuB6U!bLwogC$rKR9 zNKfa7C8+2VWPsqOU`7Or%tF`fMsOyVLFtP2h{aPd9gox0@ zJv@T#oVcAn;bfaBf7p==@He-ms}i8J&5H)8FoW##w<0LID-(h^-WBr0(rWpIpZ#T!Cs-Yq4u zQB>(7*<4WlP4stpR5t|cr~?-c8O}b`8saT!<;yOz_4Hpb9<(ODs|9&XhJ4(pN-9yi zU{)KF$=GR*F)b2f|0?3ZOB2J5DX)_rh6ai%P0&VIF7_9->wUe#&C4rtxwPyIFm4-o z&V``rrFH-cEdZ(gp8a`@P^nCfx9(LT!2Rn`E_pZtXKx6_Tp7QL%In@J`SNk8@FIy4 z!`ca0+!o2le}Cg2-n~3wy5c>%eaHhGn5Zu><6N2O1ljj>sMK9L=%?d^N*eox)J6m}Gcd3_X1k_;4HRC=7}Mv)_?rwqL3!r~XKFjz-Ed@@B`EJ*|>eu4=V(@Fr5O7E<-Bo(4>e^cpCq_z3+2N|IHKU zLUrs@e(R)O9by1Bo&uz93e?w1e0dPG6NJXAyNrzEnrS9beR-v?&`ecj;|3V;<{kO0 z^ox*}-p!Qq{5g!+7f&eJD$*AqgB8y*ZNx64D5PfN>YR8k4$0Dh3f=6&#`>zM-WC`GqE-;XGC2~AEtt(?B(7Kln(#M|cT<=kEmnT`sw(eP;O zuu+CT+?c&`r$iACAaM~jC2W^LscmR2aHr-+WqA?JL5uLm0%0lyat=;6fRe8g#L6a5 z^d|qJ{GbTsRdLkVJaseV*3keaQT_UBzvBI-OP zB&8LeN(2EihmNnq#BxIc8o?h8*Wfh0Pl8}k?gVe`cPYINLfu#~xxZfwq5bwhCOZ}~ zn?hQKeRyoKuq0(%;w-+)zNeYgEe%_JiR4(AXO8?Jkz>dU3HDsm>ORZsAIK6(6n}>} z8b=maj*pwK=CB2P55?kYn3Du^&o!zfDIW7V>{*d3v{>ciH_#JZ^!^N{Qqvuet61i8 z!~{c8%iFQX%!Vy;#*XV7U+6-%yp8$IirL?mg~V^QLHlfao!1B?&e@=bHY!5kDn!9B z)297EXyF23`N^lwN$iYWIm1jBU`K(9sm6KK$*pCW7m5TA4pofu;F-3c3=?pGGC7w_ zIU&;OpNS1s@|0w(p1iVuA;3-@gxMH|x-5i2>D~B< zPn3aq>20%`&%Ac}IIjOJuQ;u^92mpdeQ^m&D|DS@mG#1|Be>W7mu&HAT!jE+w~j0Y z;dDgO#${Nir}sSd!Q({j1EXELOCAG8H`nW%2*ZEHU1ZGnrO1UIK-y*DGO3Kw0!(94 z;XV6-7GEA2SiinHb-KNpbI29hYP8>JoKfc&VKH^5nqaDE{A2>ugu5Jan#vBb^G|ZBBM5g2Ykh_i? zFhud!CMR5`Kvo$=+jMZei(RxVNtqs(Bapx)b|E7W?*Nab!pT=|KI44IxWsItB)NL9 z+}{HapbQvU9CN~^!p1?w?|1_%5aYBuYrHji)=J^<`bY*dKU5%-aeMQW7!E)cghCz1 zAy5jap*O9sjN9J+)30yW2))vRaT{eBFtEVGT`8=J9-l81@*yKq90OVVqzH374k8gt zQQ-MomZZ}HKeOr~iklxiHVz`?ss*T%s|=jm!A@c)i?8Xlu4tl?zjGOt`&15$)TdY| zz>4PMIxP_9Tp3^W*h3p%6nJ#Kfi`va`r)@|9&|%=OZmdh%Z$5-Iz|wSkNO9E(sKyq z>p1$LOHGgtewdw3^Z=7dC_bN)JS^q45JDUG`NUvrU_7-fQn@Znp~PB-U=GC*+CJC4SVyro|6!`Fs5D z^&?+9pbyv4Rz0w~VvaJ>7%m1EPxJk@v2n4T^JuHdDkavF6_CBWG6VSzC8+e2O*G_PVy1W2Cx##3Qs_nj%;Tg#8k_3=mWttCH z^S5}650?Q5cy$_BWCm8`Yjk;gY%`TBfcqhxGeS^`nCx4@AbFtNyloLrE3yg@Ce#IV9F~EPm3vPrh|r-4F{;6ZEqG~ z*t2CL?!N}cAjk2^+g_0rPBl)vKT9QtLl8B(Ob1+bl^MUBWK+xE^{Ele-CY%wEez7> zE8@#iAucq@X2d+Rbr}B1`d%f-g`UJI@kB~d7=s@d1mIT@j5@8Ny^0nA=rBh{FM_IQ z;I;O$KwTvL&IdB-ddi}hvXA@_o%LNCdeIARs66@`EE;(+$7?9EvW|x6xtS@Gg~H3Ji8ErhvYq} zw^q?MWK>tJY$2{A=7O&By7YLy56yl`NzrL8SLzK-`5QDLJQ!6Y+$~wkJIebRqj&gX zT&lzgh^s3)@RB1R&)zUBzZjNNBMl|r_uWSeOn?~0j zddNEnb;SZ*q6NDC0J?p;qIUpir$4ANRTnm$eRu)!)*SE4Bw)|4=^nzw+2y?=ybZ*q z-J@_?jCqM|?Awu^`aux0YTi#89!U5>oG)&+o{|@yXV(N;8+9aPbU=j>omNhI)p-dP zSn0{3x2@fiqcsa5gxudR%}PpaWv$jojKp`rKpq0+(x&kwmv81pDtlrhZh`vsY{jh# zt#dMoINc2+IxNqS0Rzid(mc9Uj^a+WZPq@PDg~0 zxbOge-21rHH9@H^yD8|l7#cd_*p=|*pC*{*WfMmN^TMy{dkG#@hfE+Zf{;W6>H!o% zy7OdgQ0mF~Sac{KUVc6*cyK2`i}RFY>L6ccF6OU1A)$PJF#K!TY*j;Jq80*8LjZEH zhz45;lkkd{=WV6C6Oj_lrXZ;0(dncgepYKElBB#Zj=WwU0gBts0EQUi-un!NVXBQS zl)Px=Noc2Fa2-;#U%i%@SnG1#-e`HTq%>$Z=Qmc$o> ze6nDa6TtG}7I2+reuI4`1g~_IS=AkGjkOm(58=p09@Z{)2bk<{xnrTnO%-@iB!mG1`Mm^PC=>dyc8Ufx)pVcZz$UjwKvHINIzJCmh}z(s=$St`W_sB& z!a#2MuO=2h!FapudOP$#o@WwM6+KNUtN#7iAJT}Fj_`4-ZlP-In=L0;(k(z!wS8O2 z4h(UCxNPNeRnJ&3Y?%`lIeTbOW&li5SmvWI5}B)#xrJQotUCow_0-~X(-Yv+!<{e% z@x#u!fy`fNsJjlnG3L2aBi+bq(Anjfb2__#;G0>sI7F}k5V0`iPDHVO_!*7KpwTq|l{3)sfCfVG<8wvjg@y+cVwK7>_is& zq22@o#F@4#AB52h6i5GA?qXs8+-Bo~ZmFzq-MlK`l8Df?Jm3cX_(v<5y_YQ+n1@kt zn`i=Ax7!F%0J;@4sj}s+OEKq}`z;i(lRfaIk6DMrqSxdIwFjR! zuGJf5JU1@eY^_~%hv)SARqiP>@X{D;vAE=Y?}Y&UhIe~*wU5@z7?=zbVmuDYRL#d` zgpzSmGSI!&5jSDOevDYEEj!%kiM-AHrqvHi<{hy>IZXs~f~Qi&IEeac>IvOE*T3l2aiHKL$RP=UC{RJ(mZ zJGP2@lbsfQc~=-t!ul|7c6Ijz0H3?mn;<#vNJ)oS>M{gXl&Jv21Q@T(@+3(=mPgt1 z%Y_hkd2dcsls%4^p914w^=+lS7K6%Bw2FFp4Heqh5q#mo`sZHyI;u3hMotC1x>f|S zlajGRCKrK%&up`er8}v|9`N6-nG{;q5d87((UQS+O=)Z;Qy0o<2RJq~f0q@}AeLUS zAhjuSG^Ta|JyB`SzsYxS<5#RiUXs3R$h_-2$32O}x!?cmrOYi#W;zW^)#bCO{@o!) zFjw)P?+^3onms8=E0?qwNCh`?Vy!D2Aq#&B!?uW=X0+&z)IJ!q8g-#zc;>rD-`TJl zy?JKMX)>@YlL_*#Le>Ue*ja20Ra`xv8zdnZ4P?-SnWYuINB{*hoeHs-lX&}ku^yXS zSYXlg{Z^4s>tD{Bkc9r@lIy0MUJW6W#XDcBjoA(7D7+~9gUZ$f&Yw-=pwusDk{t;H zE`4-#27oDGqg7c7CmRP{wgeg&jaORiVNMs7RHzxg`gAm$B18n%@C179GvUD(J$)ya z!vdxzM1)l_846eh!&)#}$K>#R(SQ;GgLW#{q*)eI=Z2Cs87myo7UOexb<|(jjVgoI zA|W;GnNj2>epEu6e z6afbu!XY2oEXq5KzzPnLVp3s&isfz5X>`*5C=8d~L8TMJhxdlH0WuJtJ1KZIr8uK0 zIul$eGw4MWfc^&p3@%z@T<^cPuJ(j{L+*-e6LtvF85Dl)zn@U@-Q4teaFBKrcL;^& z0S=ps*I5Ha@r* z1ZYp*I%ANtIF|>@>VBDHSTE=2f#GX)*Xj{E@5{zJk)2uXWiL|34y8xoI!g=gEDhJ-V@^m{;k6)M}?G^y{wfQm%?SP2?oF!+B&HbIw+RqQ?&S$u%Ihj%FLpbWkzC0+@IfdAO;FOg^ zvhw|S7w;(;X_g6$tpv$A@;kFP;d@l(Uz7@eJ)KMOn=l!IchxLLjTrj`B79`sC*Wki z780NBz=r`uf3wm>+PP_%){w7_ixB#cCyP@?XXW|N_AGQSSi2Ygppe_Bo-8W}#|K&R zplB4HwxfB>*EefPiyGuumOw2^0`?UTR;2wV|8Ct!du4oC;SBg z1G6Q$sXCc08_a_3>Y^tr?!(gH$PQ6b_=TMh6|wfGrJNUgV-8`pfm@ zI$XbXdW)C%;}6vEo+dw3zjbV7GaxrmEp^d)3?ym1QrTasBZC6i9WJk%&ygS#aj7O5 zMxyeX?8zD-N+TaZ81@EHUlXm8WD~67CEUd(#Q&wynkWevlshAMrXh_UIbmo0>Vl~* zD^Kvr5QFp5(B@6D9BAn>KNID{dM((dWP|+rt)>i3CFNNG`4hNh@b~u*=E{d703P`3 zbdvQYW16Qym%~<7NZ1NhM>1n1&3j+>@NHj$g+0~MHJDs+@lNhQCWtfhL zl{9!!@T8>S4i^S|92$r7CjbNY6(Ll-PfuY2F|a~jttWtNtbW~kT2GvXDpY%m5o_Js z-(`ADMNbbw%(MUVtE?3iz&?qSkFvbnH|>m@Y?ED96NN)se|QYFPvr2K0TL74kf z=v+mjOFB*km7WOCW$K>ZcZB-d)dF!%|oDSf4FW_fiZ6I(VFWy1|n3G#Zyo zBaYOoTZj~%!zF-@CW1T$6(@+INsYj(yUWEHk26Ymfth_t+bByBwVt_PNo2Gb6F$qy z<5~|5Qd!JRz~LgxQBhd96zNfBD%X{%IK0 zKeMk90jzi_BKK|ut^U%+6z5iX$C!-d>OxYAuN#j1F9S%|JC&@^=fb=6NP-hF9W}lYGZXYwo zP5e+m^KXv|DWoqv08cr%A|nREGbMObJL)8~7V#Nftr5;Yi1Q9FHO>}nLZ+hJazcqw z9A>jdejDTz=iv>KW?x8NWMxlCBh*I~W!KM{N-xR3ngGdrFl7RYCZGWETMnNlF?3KvEIAYTqj) z%g87@RBJ1-vcyU$qERW32`n76B0sEZG7uXWoSn`|h-JJq1hkU(Fg-Ked&GFR3K=G~ z(r~Wdgw6V})RfZAhPj_!(T1_8ukh z{pQIWUeef8K%sTTuNSQou+_8IY2KC22Ro=;aL*jJeFVE_WwAeTx-e0ipj0Ul!2i7a zuTvGby^e5;`4#U81=Ro_&g1lK)uJV2xPu4r$MECJFBmivcJjXnK9IKcGv+v?Udh_j z@|O6wGIAFUo|jq)uXBlgcV-0max2G_A5cW_PbDl12zh1~3XC-l`kNHkU)1Z8@Kz8H z^^hF_Iy|Et&m05)uP*&A^F#U(P2UOr;K`mtapRgE4i%PCdaHAedxWH1f;PqYn1MqO z*Xrr4GQPcrfw51VcuG=@1FbJH-!ozYUq)PjZP5b--|S*eFI{{(-NZSGOU}6!t1fsb zy_hJpJ~-xme4iio=5tPUExzFuM`v@zr$4+?N-_=s5Y{=z>T?THK@-3kr6y`d8aToh z``I&^ZD{`nY&&|L5zu`g2p~YbCwKn&cibesNxP_hq|6=j;jEz&yg#r_`unmoMJ)`? z0<9ad$^>#VVfE?VgnE0|IEZ1^7TXjQ4Tqgxsb(!rMY+iVYdz6YMF8lO25lr&{uJm- z@q8ZdVHCY;4yEVjQFen$8&OT&IA5SQ*o<>!ML7Qvtbf!u6h7eIXaiu*wDWqu!-nin z&$ey+ABD+~dZb&SifCmm)ImJjVePTZKA*G#DC!g#_+B9CmMA&)2UO9*`uV0eN?D;? zo>F@+ij(Z+EVCV|t;7YPlb53XN?g|h=@$u8XboV1mhLNK@lJNI;>T=-t6%SRTm~@f z7SZ0i&jMx(Xu$P&(xb0T{o`g2s?tb#gl>M_La#4AY5xryHOd7eIWFC!)){PRB~-3_ z!?&^qvMrVF)+<^$y?4PDdCgVqtl~X9FYnQODTnfl{{YuoaA;GrJO~g?R1eTr}xoq3-qPqrHkKQNXEQrZ+jCH21bLp~_avy7s@PY%egc>Qf)~r-Fb8L0_1$ zy6V@1o`nXAjSm{{NtoYx^`?2)sm}ZK8*lkwhi!|TutxhEd(SJSpnIqg;<%T0)EzC} zE5Ex^Vp06TbZRLh3&M?NYFLAz=T5+b1Q|jC;u??TI`&Yyd0tlx8bXTlT1A_4TOhOw zw`k{)oZqjU)9ER$NPbOIJ&Gf!w`=rj3UEmTc99W9-VZehI0c5nQ;9*SmV`apKId6? zJ@>-MtRQEy58#B2;}mH5ZG5i?9pG9nZ7TO&Z@rlcwH-@-{jJxrrFLAGzX3+A8X+6U z91$t#r*+cb2KO}MS39$FVwSIQ3Q`+;F6wC&ERydUeZJvj6KpJUb7wm+$#T=R;4T(K zCso}PmvZV0*lAGDN6B3J#62|5fMhHX3~k~|tEr!7STsfINoXCv9-jxIr{TxtO@b}$ z>yfx4W(CjtXML4|QF~w44Xw-zReJjHiRzj#N!C%mYuwFkuetDbvkMeom`3PPhDgAr z>F`byp)Az^2xr&M=r`pOvA{py@3wp^2p7Z^t4)y{oZKs7EP?QrmF5)N;cwATX(Jfc zIAwnzF5yf(VaM(iB3A|lLH2brG?4ux;aDl(6q%Vr)qOyI?-gLxo91y}udC=gu3d=b zP;i4O^IH228%LkO!BO{yl62XNy&oIpm{v+ls)XG!;=##o^G^CW#e5EDL^fr6zal%^ zTpKJ8JckQgAQ#@9zwNQyfC3txCa|62(a+2OLT!a0c1Y{T?0U+-AHhr1~8)Bz6s(FDZ$86*9_l?lfVqE*Jv4?wgXa!X;>%(FrD)y zvjHhUU>4#5v$IsvA?|6p)2~)enHFfY#og{B2XQ{dh9%cCE#UwNf>ja=yfLQ%S23;R z>TU|xq$&&M`1$P!{81_gJ(v*Bd&sCb28rSH26~xgkb^soG>wD-G+ZgvwT(*KC?R`= z#dd#{+->A0>IPIxmP9g3F2*FwNHh!tTN%bLtqTN7l6cmIo$?x? z8n~h^!5cK~k8pksYh~`wv1xm4=aWctz}|zbDYQus)y7W9qRB8!Xf3|goLh(8b5FCg z0Y!6jFt@&tcgchza!`2yb*`PQmXrd&W$pV0p#J_JlCb$bjM@Cm88z1Tx6IIA4(x{; zQTw#Gugg?34mN1>1e3tT>QuwiN_=(*aE@d*e_5PAxhMj1pd@Dvo98%k&yM&L?W`ZO zyv1$CCd2#;U0P{9%&kvCu}WS8{-8(1sd#W!QYit};ib-a7zKjpcJnB-658G(c~+N* z^Tp59aiZ)!XmQAcxuJ~{>^VTJTn$+I){~EGO|=ejYF1h~)brmPbAe(SU5kJhvRwe z!Gr<|HwwiLSzh6IwD_3nE;`YxRWp!SoGlhG0Ou6+h7;s3VkR_>5dF!JgV(SmX6nDJ zlTpzOG}n!udMmv9KCU}HVTgH)A;zogG)EM<|T!_nVtL1dJ8M;-4)|C|&3rfyX50Y58<3XMe?Hc^| zzQwOBUuj{}cFf-lNr>h%Js(iR1U(JWfTn#^7UO)46skzoPWeJSToAVvV#I(Y)C0ZW zF)PTANOd#ED-W{2!F0fcxm|8ml)nr)b&nWHZEvHSYgn^@(n0|dE=X0T%J>Cv+ZU)P z${@ts#~EbDbqw^t9z{$;#cdg^e3R^LX-v`F+BS{@WQ6$$4+XP;+xL+Ai~!Gz@zq>) zaWwkTo_Ht%_IMv95o;}Yc^;svYwQF%YVHA5b7O@KZi~eB1HjP`x$8WMyci+@i^r>= zy#Ml_DeKDExAeN~-S-LM1k4uh093WDyqonFrFMgU*=b!NB*fEL`^q(K{|3QjmcI%O2cqp1DKlhXuv%$xO! zBM!rN&SELYSiV6NISlxK|Ken-30F3&lsU;O+^7{7IfT|+=c$>sAyg%3Sr6p{G_}4^ z!L`Gh?y)%(-EL&ki!VZLiQQo!9cYE?cgb{~j|%f`)J3|lV#HXX!2;`nNJ)P zB>EZyBq!j=e0YvrQw-a!>w;d#5$FID$7Ux23SAY-0j`t5KcFQGQ=?%RV13D@ zjP>teb|g;qO5M9*-E1`EX>ux|DM*(KUR*DUIrvT3G8K`#QwO6C0uEg7AB2?EemN_3maaE}ZIwMf)kY`uT?P`}^!jl%{bU% zqeL~@#a1?DEz!_K4W+y~ae2UT)k6Q5E;~#Y;1m`#6pAjJI!_G8?G|1sjK>VV_UlOskmV>7F?P3don>Rw`LC7!8z1yH*U6FqeBH&v|!3lpx{2FYdnU-^`H{4Q2dr#tBc#rm&H!G__n7W}9= z|NRP?g<60E)a(q{7wG;42I^)b%hkqg;)tv_1>B+_?@yz%C>>(G#dnm1s4)EZC{B2S zhpf2E>H-sIz1dm$vzA6jprfCL)-A~WBHKj9vb z_F2eDa!L3L0zhJeJ`%72u_J~%oP6^{qQlY06TZL-B2AEmP_Spo4HLS$)Q-!RhmOMo zJ%L~f^h7GUwr6NuNOLwbe&|40`{M{7R+H!BxsJacpba1Ze1q!~k{T-av>MuLRGU~A zeQM?7D5xaL_?I1{K?n#I-0Ye#$%Ao%7TdaCvNV57CS13z6(yDxoVHkg=GEg2eHsQ> zX6I)pa~y4|2i&*VCR_BA?-YaBIZuSbI+YNbRI*PBLaeyxV=$Ng)*bp5^dl)Ks*2CK zUW%Sg8p9Y)jUhQjwCe~Lg{O{{&w2r?H~){%hIXISVB6~+u}QcA+(0<<=eCYy<&8f6 z&!T#%YJB@iu_J<~Hpv+cne(dtpnv+Chi;W$AXUS?Z&~6J271kl&H~4BJhjvlu08`2 z1K)h>OjbM@)WrPoow3z2NfHFU)B6fCU8X42(~W`@V#W0o_08eUz;Yd{xBduB3=ACu z%b?bRAKe*Z4-aY$UNn|BDgIM6h>b`j9F@ft)Uy{v`>X^E0We!MUb{DR%?wd1=RWwH z3*ekhdch4oJlR?p8tFJjABO*y_I&mRD)7<^v_`@um9nKa%A z&K}4VfBGwv#+226K#^vb8b&E7n>=xL-w$?6a9d)4iVVAZjzljE)?qZrAzed!&$ZbL zf9c%`T%ATuJyGttQ!1RU zUWgQB0o*{w@QJD00J(os)Tc>fCIV17;H&UM@2d|y-1?kyXkrF+#??GVDL z{|GM6tj6!_0^yXZWXHGzi5KNEq5JiO4)_ZK(Yr?jHGofi4s&;CcN3X3Kw9s%-#g5d zmJw0_ygjln{r1((%2_K@^ah+oMnmBV@9=$)3A|LlfhR$jIHSp8{W<^k5E?^`S*ZrL z!3kiY-oZU9sHl?VuX)PRWhyEY9j1Ytx6m7AZI;EGP_%-RMy>HCgRWCzslfZa2$?$g zhA8J%IkUXlA~t?r3Y+f*nIzs*9ZGf}p=w8~sE)i-bgdGqOQxHwR8BSUGzy(9)Erh5|vPyMn!WZ7*_ZQd6O>4>F2;D!^f)DB92|jX2lq0SZ$@etS#3B zTfrkC3$lX?X+3jZto49q^i#ZDe;Jm53<6h=GAHm6&u`tR&{&eA>CC50G_?(2f)=XfooUhGjmwynw& zjA1A09v#dC9p78O(}6vLDd|L2Pj-Lq5~eWHv>E3p{z{{hR~+yzN$#rml!#LSBd4eY zut0%Vz}>Rk^xh`&uei`Zy_}B+KFM*!1~LvFCQmF7`;{wu0>EVay7V9rbx&rGy36IQ zZ{2$xhacE(&TkaB*id?b?gXjD5C6I)77$!svgfj_%b(tr8Fc#wEwABu;}QJSc-W7h z!>RQIqS!MA#z*F$A^&X;FQ52Xuw>>>P$eO5);(Whs|_a!*|0>uXpS+HJt{Ark?|m- zN+l6Y4TzZR{2?f3^c`0;8w0`x`M2`zl{l&}MjvVs1P7fnljc?CA?-358l}Bat9PWA zTusvsue)U@mNHhG<(9c;_ugpRxD0dNgyv2VXFsWg^8j^&S&hcyiH$v=o@m*^;V|Mi zI0Wh3iOyln&G0C87;x{}?2O;Kb}H8dq>quVK%O#djATvs=87K?V)6s+krlfG(MfWM zEBqF8$!gJnjDDEPG{2+8{s}1p3!mmk|ZP3Cv>PHA|kI1P|o= zk%MVXXYbDsY6>i*2{L^lK5P}g2)O1GVC{1_{(pmp+Zyges1s&w_5?=Ayc64c*fkG; zmK-)05_LYh^decrHL57aIZQ7BGG4vP zY+JJBt0UZ0o(fn_%EV)pOV0Llyruj*j&(+RXs&s@^~|#N6DG|O2$*9M$CQ zPL|?N#hMftQk-V@N*b`k$GbY!krHRm?2NbJ%5u+|hw?Q8lYcny5FB4@+bs+jaF~17 zOT|aLHuJxgfMtHVf@#CWtNUxU&%n^#-T61eNzSGVw&}MRP5g9GLBx&bTc>H?gPYst z;FPEQSXue@piah(N2!L5h zbGz85oBHKa2J1`P< z_2)?q-Z_nOmJ9g=3BVGX0Nhl08Cr5bTRil&_F#YH_9N2&|6oZClBonGePG>5+v;Bz*V=-V# z9@q@5FiflQ9$CXhWx%$v1_7^@fn5*J%o9sWE`CL?2Qtxw46Ch1g*jCfS}W(GLR>Ea zm0#bVf{f+Rv_rD`%H2OMN=Y7Pq<9G{Hf*`g_s_hP7u$zY(OLNBaCbZ%MGfVyq`t>WDp?Oz=OMwVPg|wg1YHMfG zTgi^|N<=qBR{ZejcXY>L}a>&8I+{X>f zeNsU2s^EhIDLeuZkjz95Z@RcghPLo8`g2bVj=^pLh8XXMtAr08KBK0jR|944G=twD z4;59<-I{>1ROKQX&uD zc^WI)w3I9Ye-kS+kEmM)pEgAXxU-yrzEu7@^p-9p7Uccr6K=}{vzGGk-)6y`-`KDb9*sQBUg$K zS=kB~9FQd~Vne6$FoIBJb;E}VG`IZYpkFw%e~8hRa1ojG`~upT8?b>2?UD@$`OYd_ z?0pc&;!(z7+}2kp<5S`berYP@#G*HW--JowyjW`a%6&KRisaqoGyRtXYF21Eql@iM zvL@|}7KQf-24A3RhTD-d+(-hzEm!CG?mJMnV{tjR^}IM%3je9pwCVXvHW- zvQV$ECd2pJFJk|PeX3SZ|0{H!-5e)MP{+j&E12>5%hk^K-3GA6R~gpSCqnAMQn8hF zs_5E^p;iNTQH|`PCeSVwIr7uxxfHqyGWBaG&#a(p+a3F85-Sf%K5BTR6>0C8UYZ(Q zomGQ=f>ojO;zJ~pvc8hK4y5=C&oQDfN?~=f=z(1_RrH`Kho}DSVwN;gWDwYL?T_(% z$7=)XtdKXO4My;fi@$PWhd zo>q+zkX@X)Tfgz+B`UtsgB*bkFdxPUDVW#yb_5gpA&_7MQq{%?cDRFos*YN5HYk7$u^yvW}I{e9cC#5{m^3O3g_ee^LRF+6m;w1>yh+Lv&lh8^ z?i*`*`|KgUj)UD7e`vm3sh`sn=xL*a&1NS<&pf`;liHwbu;CFpWHj{G878_-mYCo*7A;1`C0uV30adqGq zLt-NjrYwM$76B)GH-vdGD7d%7Rlkl=8A!UWv=Iawv-#-J9_ZwDoZ240@Vev!zw+@q zVq5P6%8(MQ$YiRDq3@YTOp-@wW3wQ6aAe;|rBbY0sf%OxI|!Gye~K;wqF3nmQ5zCY zWC5gfvEblwGOSs)s+nIzO(`7u^@Iw*aM!6eKgl#4^uwmTH(SXN(O%u^Bkce$D<sqEF99Ct2 zFgp!cM7BPHHD>7u-d#sl@x}C!O*DBI^i5V~JzC7z)GE`o6Xy)F-iRGnL~FSnd(ISc z_c#sJHEePw5+cEb(dM|ihu$>{&=<)dzQ?M|@ySuWxZRobNJG?Y9;etL9Gg*=BWCz) zroBF+hTn#@9sgBAPpCl^+#$yiA3|I_Dy&5!(FJ zK&ON38>5m0yrX|*=@IeY&Anf4=A8UC;tM7bh#G2EvYaZbbWM5L73&RX$~RYIX%`z% z<{ZNlZsGY=KJY!e5FJDPrJw8aVL={FYMG!fyc*f;v8N$R(O0eFeM9QJv#^@gTe1=v z(7%`;^(7$s)R|RY5-d=vf%e!5Ge~IqPu7ajS*)bTiz?0J`DL#YZ8FZTa3&86`rI## zPX5wmUP4sz76JS0H5*3`$F*;L;2%4Xnf7lwzM$p>AKjU)t&uLBek=k8`#`lhrJ*k| z#g0}|c2J#vkX{l8%~+_&)g)mpVc9!7fC+lKZ4`N(s}fkJi-SoKF<{5>1~$dDnwo4t z|K>xqCC|zM_B~W%Uu*Vj-<|=E=(v+mpP&JjtDT5@4FIGSKb?!}eIsNE8ish#{BXbe zU!O$w>2)TAQ=2WtZR$n4#ESEvQkVAL@~WKjrYezqaKxt@)?G$7*o%1 z%-{G9(t5Xg5H3)Uw^LWSU^11!eV@}uZpb_jE_$cNZHbuUgaE3fsIPQL5dF-6{evMY zz1R}_AtmL;GIOWa!=LMHkS)}0z5b(88_OVd*9CgD&RZBAR% z{2E#6TMC5 z;%+IWg?hy5-d1ejeZ|nA0@vjh_Ea05IzM3B)&h|SB|C3v=)7s? zft^$|luJy3ZI&~W#{tpBYmzA8{M8yc626)I>+F#fMYz8JI_|gLFJtCeF<{zrp&{p9 z9I-yH@UQK@G)kya9+!8#{wW-{FvP$ewK?_#Qu0jfQ0kIBz88 z4&BX!hn)3;mRfwO`{HS9m)05Uj10pQyY{C%9udI=&HtZ#>#-jO!7;YRAg7|Kp-eCK)hxraRio`4k_6 z_S=tW9!S|W;9*xg28C|D@w%4qDR1@u60bPYgpe1Qd$b=%&q(CE8g+@r)Y!6>~I zoa^8i5nSiw9&vAoyEF>?37!N*!{QraWYP7gr6pIttM}Lu#=6~@6boLpd!$ryM8nqA97+`K-{Z|2LYGX2u z54<3WyfIbou9^8_QEXmp(8)bfWb!5$+Sj%sVY=*SVVBM8p#$Xuu-H?F3JjVp37GLW z;X!O!&AE!dpbT$9F?cDTG$`%>ULUM#2i@wU7YA{5$CC)TM=zl-!Cw5TmgF2;HiMgI ziqrJ{eHO&#l!0H7=Q3>vXQte}O#{#ncNhU*l%2$*wJ@b+L)bd0{pNti9`FeMUoB#% zK^D=wUik;lqy5qX->b3%7yFYh{}Cu5B7?cr>yNxrK-|bSjN8)PY(rh199ZgeyAvx) zKFL#Vc)@}e6vK(e@@W@5?JPd()1+nnd2mG4<*v~?Xy>bJe@D6`Sr?=O?k~D0=U49b zVBs1B;t^T+EoNWZmb~Zg#{}^fh|@k$A*Pp(7>>3ct{P7gEJB^H8Zt4oe$=6576qvl z#^)-2njAD#*@^IkmjkJNlF|>!cq?MoJ5pIWEhiZEvMe}1f6vPU#EE`7#{h%?^OoVT zCtrjQlTE(B-vy3@DBlEPq!pyZWZqXdVZf837DHaje)_kF!4yyuB;t z(JMJkTQAeARPrbouNl>>cCZ7)r=yK~Yg|3QG#QZ1YEYJ;!DfVm_yvT-mTvw7avOzl z+qE~^Ze1P>EXdjZy3;^NcTy758=|bcMtKJv{yZ%z>wqYN zr7dg-V!x?hU3(+UlU4scTG-^Pq8BrkCYf-gCTi22r--JaMPF#cFMHo&J_*BKt08>| z>q2m42K#nM^c;>PW@8_ZVB%-%tW!OdlFqGmp-9OK8Wv6Xsq;#_dfbgOejIRl& z62~7{AmaHAhyBA{Ne`59mkdIzst+Fzw(0L!*$)lreb>3fA^t%Mn`&n%8?apih@x!}8U#kJm68WrjQmtip6Dy| zVr@4Y�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 { - let mut e = $projective::identity(); - - let mut v = vec![]; - { - let mut expected = $expected; - for _ in 0..1000 { - let e_affine = $affine::from(e); - let encoded = e_affine.$serialize(); - v.extend_from_slice(&encoded[..]); - - let mut decoded = encoded; - let len_of_encoding = decoded.len(); - (&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]); - expected = &expected[len_of_encoding..]; - let decoded = $affine::$deserialize(&decoded).unwrap(); - assert_eq!(e_affine, decoded); - - e = &e + &$projective::generator(); - } - } - - assert_eq!(&v[..], $expected); - }; -} - -#[test] -fn g1_uncompressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat"); - test_vectors!( - G1Projective, - G1Affine, - to_uncompressed, - from_uncompressed, - bytes - ); -} - -#[test] -fn g1_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); - test_vectors!( - G1Projective, - G1Affine, - to_compressed, - from_compressed, - bytes - ); -} - -#[test] -fn g2_uncompressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat"); - test_vectors!( - G2Projective, - G2Affine, - to_uncompressed, - from_uncompressed, - bytes - ); -} - -#[test] -fn g2_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); - test_vectors!( - G2Projective, - G2Affine, - to_compressed, - from_compressed, - bytes - ); -} - -#[test] -fn test_pairing_result_against_relic() { - /* - Sent to me from Diego Aranha (author of RELIC library): - 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 - 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F - 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 - 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F - 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 - 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 - 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D - 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A - 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 - 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 - 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF - 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 - */ - - let a = G1Affine::generator(); - let b = G2Affine::generator(); - - use super::fp::Fp; - use super::fp12::Fp12; - use super::fp2::Fp2; - use super::fp6::Fp6; - - let res = pairing(&a, &b); - - let prep = G2Prepared::from(b); - - assert_eq!( - res, - multi_miller_loop(&[(&a, &prep)]).final_exponentiation() - ); - - assert_eq!( - res.0, - Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1972_e433_a01f_85c5, - 0x97d3_2b76_fd77_2538, - 0xc8ce_546f_c96b_cdf9, - 0xcef6_3e73_66d4_0614, - 0xa611_3427_8184_3780, - 0x13f3_448a_3fc6_d825, - ]), - c1: Fp::from_raw_unchecked([ - 0xd263_31b0_2e9d_6995, - 0x9d68_a482_f779_7e7d, - 0x9c9b_2924_8d39_ea92, - 0xf480_1ca2_e131_07aa, - 0xa16c_0732_bdbc_b066, - 0x083c_a4af_ba36_0478, - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x59e2_61db_0916_b641, - 0x2716_b6f4_b23e_960d, - 0xc8e5_5b10_a0bd_9c45, - 0x0bdb_0bd9_9c4d_eda8, - 0x8cf8_9ebf_57fd_aac5, - 0x12d6_b792_9e77_7a5e, - ]), - c1: Fp::from_raw_unchecked([ - 0x5fc8_5188_b0e1_5f35, - 0x34a0_6e3a_8f09_6365, - 0xdb31_26a6_e02a_d62c, - 0xfc6f_5aa9_7d9a_990b, - 0xa12f_55f5_eb89_c210, - 0x1723_703a_926f_8889, - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x9358_8f29_7182_8778, - 0x43f6_5b86_11ab_7585, - 0x3183_aaf5_ec27_9fdf, - 0xfa73_d7e1_8ac9_9df6, - 0x64e1_76a6_a64c_99b0, - 0x179f_a78c_5838_8f1f, - ]), - c1: Fp::from_raw_unchecked([ - 0x672a_0a11_ca2a_ef12, - 0x0d11_b9b5_2aa3_f16b, - 0xa444_12d0_699d_056e, - 0xc01d_0177_221a_5ba5, - 0x66e0_cede_6c73_5529, - 0x05f5_a71e_9fdd_c339, - ]) - } - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xd30a_88a1_b062_c679, - 0x5ac5_6a5d_35fc_8304, - 0xd0c8_34a6_a81f_290d, - 0xcd54_30c2_da37_07c7, - 0xf0c2_7ff7_8050_0af0, - 0x0924_5da6_e2d7_2eae, - ]), - c1: Fp::from_raw_unchecked([ - 0x9f2e_0676_791b_5156, - 0xe2d1_c823_4918_fe13, - 0x4c9e_459f_3c56_1bf4, - 0xa3e8_5e53_b9d3_e3c1, - 0x820a_121e_21a7_0020, - 0x15af_6183_41c5_9acc, - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7c95_658c_2499_3ab1, - 0x73eb_3872_1ca8_86b9, - 0x5256_d749_4774_34bc, - 0x8ba4_1902_ea50_4a8b, - 0x04a3_d3f8_0c86_ce6d, - 0x18a6_4a87_fb68_6eaa, - ]), - c1: Fp::from_raw_unchecked([ - 0xbb83_e71b_b920_cf26, - 0x2a52_77ac_92a7_3945, - 0xfc0e_e59f_94f0_46a0, - 0x7158_cdf3_7860_58f7, - 0x7cc1_061b_82f9_45f6, - 0x03f8_47aa_9fdb_e567, - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8078_dba5_6134_e657, - 0x1cd7_ec9a_4399_8a6e, - 0xb1aa_599a_1a99_3766, - 0xc9a0_f62f_0842_ee44, - 0x8e15_9be3_b605_dffa, - 0x0c86_ba0d_4af1_3fc2, - ]), - c1: Fp::from_raw_unchecked([ - 0xe80f_f2a0_6a52_ffb1, - 0x7694_ca48_721a_906c, - 0x7583_183e_03b0_8514, - 0xf567_afdd_40ce_e4e2, - 0x9a6d_96d2_e526_a5fc, - 0x197e_9f49_861f_2242, - ]) - } - } - } - ); -} diff --git a/bls12_381/src/util.rs b/bls12_381/src/util.rs deleted file mode 100644 index bd25dd06a2..0000000000 --- a/bls12_381/src/util.rs +++ /dev/null @@ -1,174 +0,0 @@ -/// Compute a + b + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + (b as u128) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a - (b + borrow), returning the result and the new borrow. -#[inline(always)] -pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { - let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a + (b * c) + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -macro_rules! impl_add_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: &'b $rhs) -> $output { - &self + rhs - } - } - - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - &self + &rhs - } - } - }; -} - -macro_rules! impl_sub_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Sub<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: &'b $rhs) -> $output { - &self - rhs - } - } - - impl<'a> Sub<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - self - &rhs - } - } - - impl Sub<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - &self - &rhs - } - } - }; -} - -macro_rules! impl_binops_additive_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl_add_binop_specify_output!($lhs, $rhs, $output); - impl_sub_binop_specify_output!($lhs, $rhs, $output); - }; -} - -macro_rules! impl_binops_multiplicative_mixed { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: &'b $rhs) -> $output { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - &self * &rhs - } - } - }; -} - -macro_rules! impl_binops_additive { - ($lhs:ident, $rhs:ident) => { - impl_binops_additive_specify_output!($lhs, $rhs, $lhs); - - impl SubAssign<$rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: $rhs) { - *self = &*self - &rhs; - } - } - - impl AddAssign<$rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: $rhs) { - *self = &*self + &rhs; - } - } - - impl<'b> SubAssign<&'b $rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: &'b $rhs) { - *self = &*self - rhs; - } - } - - impl<'b> AddAssign<&'b $rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: &'b $rhs) { - *self = &*self + rhs; - } - } - }; -} - -macro_rules! impl_binops_multiplicative { - ($lhs:ident, $rhs:ident) => { - impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); - - impl MulAssign<$rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: $rhs) { - *self = &*self * &rhs; - } - } - - impl<'b> MulAssign<&'b $rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: &'b $rhs) { - *self = &*self * rhs; - } - } - }; -} diff --git a/ff/.gitignore b/ff/.gitignore deleted file mode 100644 index 4308d82204..0000000000 --- a/ff/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/ff/Cargo.toml b/ff/Cargo.toml deleted file mode 100644 index e081e84c12..0000000000 --- a/ff/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ff" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -description = "Library for building and interfacing with finite fields" -readme = "README.md" -documentation = "https://docs.rs/ff/" -homepage = "https://github.com/ebfull/ff" -license = "MIT/Apache-2.0" -repository = "https://github.com/ebfull/ff" -edition = "2018" - -[dependencies] -byteorder = { version = "1", default-features = false } -ff_derive = { version = "0.7", path = "ff_derive", optional = true } -rand_core = { version = "0.5", default-features = false } -subtle = { version = "2.2.1", default-features = false, features = ["i128"] } - -[features] -default = ["std"] -derive = ["ff_derive"] -std = [] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/ff/LICENSE-APACHE b/ff/LICENSE-APACHE deleted file mode 100644 index 1e5006dc14..0000000000 --- a/ff/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/ff/LICENSE-MIT b/ff/LICENSE-MIT deleted file mode 100644 index ed3a13fdd9..0000000000 --- a/ff/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Sean Bowe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/ff/README.md b/ff/README.md deleted file mode 100644 index 4fbe19028b..0000000000 --- a/ff/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# ff - -`ff` is a finite field library written in pure Rust, with no `unsafe{}` code. - -## Disclaimers - -* This library does not provide constant-time guarantees. - -## Usage - -Add the `ff` crate to your `Cargo.toml`: - -```toml -[dependencies] -ff = "0.7" -``` - -The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. -See the **[documentation](https://docs.rs/ff/)** for more. - -### #![derive(PrimeField)] - -If you need an implementation of a prime field, this library also provides a procedural -macro that will expand into an efficient implementation of a prime field when supplied -with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is -also quadratic nonresidue. - -First, enable the `derive` crate feature: - -```toml -[dependencies] -ff = { version = "0.7", features = ["derive"] } -``` - -And then use the macro like so: - -```rust -extern crate rand; -#[macro_use] -extern crate ff; - -#[derive(PrimeField)] -#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] -#[PrimeFieldGenerator = "7"] -struct Fp(FpRepr); -``` - -And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement -`SqrtField` if supported. The library implements `FpRepr` itself and derives -`PrimeFieldRepr` for it. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml deleted file mode 100644 index e1e26bc563..0000000000 --- a/ff/ff_derive/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ff_derive" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -description = "Procedural macro library used to build custom prime field implementations" -documentation = "https://docs.rs/ff/" -homepage = "https://github.com/ebfull/ff" -license = "MIT/Apache-2.0" -repository = "https://github.com/ebfull/ff" -edition = "2018" - -[lib] -proc-macro = true - -[dependencies] -addchain = "0.2" -num-bigint = "0.3" -num-traits = "0.2" -num-integer = "0.1" -proc-macro2 = "1" -quote = "1" -syn = "1" - -[badges] -maintenance = { status = "passively-maintained" } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs deleted file mode 100644 index c5c811ab3c..0000000000 --- a/ff/ff_derive/src/lib.rs +++ /dev/null @@ -1,1328 +0,0 @@ -#![recursion_limit = "1024"] - -extern crate proc_macro; -extern crate proc_macro2; - -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::{One, ToPrimitive, Zero}; -use quote::quote; -use quote::TokenStreamExt; -use std::iter; -use std::str::FromStr; - -mod pow_fixed; - -enum ReprEndianness { - Big, - Little, -} - -impl FromStr for ReprEndianness { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "big" => Ok(ReprEndianness::Big), - "little" => Ok(ReprEndianness::Little), - _ => Err(()), - } - } -} - -impl ReprEndianness { - fn repr_endianness(&self) -> proc_macro2::TokenStream { - match self { - ReprEndianness::Big => quote! {::byteorder::BigEndian}, - ReprEndianness::Little => quote! {::byteorder::LittleEndian}, - } - } - - fn modulus_repr(&self, modulus: &BigUint, bytes: usize) -> Vec { - match self { - ReprEndianness::Big => { - let buf = modulus.to_bytes_be(); - iter::repeat(0) - .take(bytes - buf.len()) - .chain(buf.into_iter()) - .collect() - } - ReprEndianness::Little => { - let mut buf = modulus.to_bytes_le(); - buf.extend(iter::repeat(0).take(bytes - buf.len())); - buf - } - } - } - - fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { - let read_repr = match self { - ReprEndianness::Big => quote! { - ::byteorder::BigEndian::read_u64_into(r.as_ref(), &mut inner[..]); - inner.reverse(); - }, - ReprEndianness::Little => quote! { - ::byteorder::LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); - }, - }; - - quote! { - use ::byteorder::ByteOrder; - - let r = { - let mut inner = [0u64; #limbs]; - #read_repr - #name(inner) - }; - - if r.is_valid() { - Some(r * R2) - } else { - None - } - } - } - - fn to_repr( - &self, - repr: &syn::Ident, - mont_reduce_self_params: &proc_macro2::TokenStream, - limbs: usize, - ) -> proc_macro2::TokenStream { - let bytes = limbs * 8; - - let write_repr = match self { - ReprEndianness::Big => quote! { - r.0.reverse(); - ::byteorder::BigEndian::write_u64_into(&r.0, &mut repr[..]); - }, - ReprEndianness::Little => quote! { - ::byteorder::LittleEndian::write_u64_into(&r.0, &mut repr[..]); - }, - }; - - quote! { - use ::byteorder::ByteOrder; - - let mut r = *self; - r.mont_reduce( - #mont_reduce_self_params - ); - - let mut repr = [0u8; #bytes]; - #write_repr - #repr(repr) - } - } - - fn iter_be(&self) -> proc_macro2::TokenStream { - match self { - ReprEndianness::Big => quote! {self.0.iter()}, - ReprEndianness::Little => quote! {self.0.iter().rev()}, - } - } -} - -#[proc_macro_derive( - PrimeField, - attributes(PrimeFieldModulus, PrimeFieldGenerator, PrimeFieldReprEndianness) -)] -pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Parse the type definition - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - - // We're given the modulus p of the prime field - let modulus: BigUint = fetch_attr("PrimeFieldModulus", &ast.attrs) - .expect("Please supply a PrimeFieldModulus attribute") - .parse() - .expect("PrimeFieldModulus should be a number"); - - // We may be provided with a generator of p - 1 order. It is required that this generator be quadratic - // nonresidue. - // TODO: Compute this ourselves. - let generator: BigUint = fetch_attr("PrimeFieldGenerator", &ast.attrs) - .expect("Please supply a PrimeFieldGenerator attribute") - .parse() - .expect("PrimeFieldGenerator should be a number"); - - // Field element representations may be in little-endian or big-endian. - let endianness = fetch_attr("PrimeFieldReprEndianness", &ast.attrs) - .expect("Please supply a PrimeFieldReprEndianness attribute") - .parse() - .expect("PrimeFieldReprEndianness should be 'big' or 'little'"); - - // The arithmetic in this library only works if the modulus*2 is smaller than the backing - // representation. Compute the number of limbs we need. - let mut limbs = 1; - { - let mod2 = (&modulus) << 1; // modulus * 2 - let mut cur = BigUint::one() << 64; // always 64-bit limbs for now - while cur < mod2 { - limbs += 1; - cur <<= 64; - } - } - - // The struct we're deriving for must be a wrapper around `pub [u64; limbs]`. - if let Some(err) = validate_struct(&ast, limbs) { - return err.into(); - } - - // Generate the identifier for the "Repr" type we must construct. - let repr_ident = syn::Ident::new( - &format!("{}Repr", ast.ident), - proc_macro2::Span::call_site(), - ); - - let mut gen = proc_macro2::TokenStream::new(); - - let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( - &ast.ident, - &repr_ident, - &modulus, - &endianness, - limbs, - generator, - ); - - gen.extend(constants_impl); - gen.extend(prime_field_repr_impl(&repr_ident, &endianness, limbs * 8)); - gen.extend(prime_field_impl( - &ast.ident, - &repr_ident, - &modulus, - &endianness, - limbs, - sqrt_impl, - )); - - // Return the generated impl - gen.into() -} - -/// Checks that `body` contains `pub [u64; limbs]`. -fn validate_struct(ast: &syn::DeriveInput, limbs: usize) -> Option { - // The body should be a struct. - let variant_data = match &ast.data { - syn::Data::Struct(x) => x, - _ => { - return Some( - syn::Error::new_spanned(ast, "PrimeField derive only works for structs.") - .to_compile_error(), - ) - } - }; - - // The struct should contain a single unnamed field. - let fields = match &variant_data.fields { - syn::Fields::Unnamed(x) if x.unnamed.len() == 1 => x, - _ => { - return Some( - syn::Error::new_spanned( - &ast.ident, - format!( - "The struct must contain an array of limbs. Change this to `{}([u64; {}])`", - ast.ident, limbs, - ), - ) - .to_compile_error(), - ) - } - }; - let field = &fields.unnamed[0]; - - // The field should be an array. - let arr = match &field.ty { - syn::Type::Array(x) => x, - _ => { - return Some( - syn::Error::new_spanned( - field, - format!( - "The inner field must be an array of limbs. Change this to `[u64; {}]`", - limbs, - ), - ) - .to_compile_error(), - ) - } - }; - - // The array's element type should be `u64`. - if match arr.elem.as_ref() { - syn::Type::Path(path) => path - .path - .get_ident() - .map(|x| x.to_string() != "u64") - .unwrap_or(true), - _ => true, - } { - return Some( - syn::Error::new_spanned( - arr, - format!( - "PrimeField derive requires 64-bit limbs. Change this to `[u64; {}]", - limbs - ), - ) - .to_compile_error(), - ); - } - - // The array's length should be a literal int equal to `limbs`. - let lit_int = match match &arr.len { - syn::Expr::Lit(expr_lit) => match &expr_lit.lit { - syn::Lit::Int(lit_int) => Some(lit_int), - _ => None, - }, - _ => None, - } { - Some(x) => x, - _ => { - return Some( - syn::Error::new_spanned( - arr, - format!("To derive PrimeField, change this to `[u64; {}]`.", limbs), - ) - .to_compile_error(), - ) - } - }; - if lit_int.base10_digits() != limbs.to_string() { - return Some( - syn::Error::new_spanned( - lit_int, - format!("The given modulus requires {} limbs.", limbs), - ) - .to_compile_error(), - ); - } - - // The field should not be public. - match &field.vis { - syn::Visibility::Inherited => (), - _ => { - return Some( - syn::Error::new_spanned(&field.vis, "Field must not be public.").to_compile_error(), - ) - } - } - - // Valid! - None -} - -/// Fetch an attribute string from the derived struct. -fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { - for attr in attrs { - if let Ok(meta) = attr.parse_meta() { - match meta { - syn::Meta::NameValue(nv) => { - if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) { - match nv.lit { - syn::Lit::Str(ref s) => return Some(s.value()), - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - - None -} - -// Implement the wrapped ident `repr` with `bytes` bytes. -fn prime_field_repr_impl( - repr: &syn::Ident, - endianness: &ReprEndianness, - bytes: usize, -) -> proc_macro2::TokenStream { - let repr_iter_be = endianness.iter_be(); - - quote! { - #[derive(Copy, Clone)] - pub struct #repr(pub [u8; #bytes]); - - impl ::subtle::ConstantTimeEq for #repr { - fn ct_eq(&self, other: &#repr) -> ::subtle::Choice { - self.0 - .iter() - .zip(other.0.iter()) - .map(|(a, b)| a.ct_eq(b)) - .fold(1.into(), |acc, x| acc & x) - } - } - - impl ::core::cmp::PartialEq for #repr { - fn eq(&self, other: &#repr) -> bool { - use ::subtle::ConstantTimeEq; - self.ct_eq(other).into() - } - } - - impl ::core::cmp::Eq for #repr { } - - impl ::core::default::Default for #repr { - fn default() -> #repr { - #repr([0u8; #bytes]) - } - } - - impl ::core::fmt::Debug for #repr - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in #repr_iter_be { - write!(f, "{:02x}", *i)?; - } - - Ok(()) - } - } - - impl ::core::fmt::Display for #repr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in #repr_iter_be { - write!(f, "{:02x}", *i)?; - } - - Ok(()) - } - } - - impl AsRef<[u8]> for #repr { - #[inline(always)] - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for #repr { - #[inline(always)] - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - } -} - -/// Convert BigUint into a vector of 64-bit limbs. -fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec { - let m = BigUint::one() << 64; - let mut ret = vec![]; - - while v > BigUint::zero() { - let limb: BigUint = &v % &m; - ret.push(limb.to_u64().unwrap()); - v >>= 64; - } - - while ret.len() < limbs { - ret.push(0); - } - - assert!(ret.len() == limbs); - - ret -} - -/// Convert BigUint into a tokenized vector of 64-bit limbs. -fn biguint_to_u64_vec(v: BigUint, limbs: usize) -> proc_macro2::TokenStream { - let ret = biguint_to_real_u64_vec(v, limbs); - quote!([#(#ret,)*]) -} - -fn biguint_num_bits(mut v: BigUint) -> u32 { - let mut bits = 0; - - while v != BigUint::zero() { - v >>= 1; - bits += 1; - } - - bits -} - -/// BigUint modular exponentiation by square-and-multiply. -fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { - let mut ret = BigUint::one(); - - for i in exp - .to_bytes_be() - .into_iter() - .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) - { - ret = (&ret * &ret) % modulus; - if i { - ret = (ret * &base) % modulus; - } - } - - ret -} - -#[test] -fn test_exp() { - assert_eq!( - exp( - BigUint::from_str("4398572349857239485729348572983472345").unwrap(), - &BigUint::from_str("5489673498567349856734895").unwrap(), - &BigUint::from_str( - "52435875175126190479447740508185965837690552500527637822603658699938581184513" - ) - .unwrap() - ), - BigUint::from_str( - "4371221214068404307866768905142520595925044802278091865033317963560480051536" - ) - .unwrap() - ); -} - -fn prime_field_constants_and_sqrt( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - endianness: &ReprEndianness, - limbs: usize, - generator: BigUint, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { - let modulus_num_bits = biguint_num_bits(modulus.clone()); - - // The number of bits we should "shave" from a randomly sampled reputation, i.e., - // if our modulus is 381 bits and our representation is 384 bits, we should shave - // 3 bits from the beginning of a randomly sampled 384 bit representation to - // reduce the cost of rejection sampling. - let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone()); - - // Compute R = 2**(64 * limbs) mod m - let r = (BigUint::one() << (limbs * 64)) % modulus; - - // modulus - 1 = 2^s * t - let mut s: u32 = 0; - let mut t = modulus - BigUint::from_str("1").unwrap(); - while t.is_even() { - t >>= 1; - s += 1; - } - - // Compute 2^s root of unity given the generator - let root_of_unity = - biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs); - let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs); - - let sqrt_impl = - if (modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { - // Addition chain for (r + 1) // 4 - let mod_plus_1_over_4 = pow_fixed::generate( - "e! {self}, - (modulus + BigUint::from_str("1").unwrap()) >> 2, - ); - - quote! { - use ::subtle::ConstantTimeEq; - - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = { - #mod_plus_1_over_4 - }; - - ::subtle::CtOption::new( - sqrt, - (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - // Addition chain for (t - 1) // 2 - let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); - - quote! { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - use ::subtle::{ConditionallySelectable, ConstantTimeEq}; - - // w = self^((t - 1) // 2) - let w = { - #t_minus_1_over_2 - }; - - let mut v = S; - let mut x = *self * &w; - let mut b = x * &w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: ::subtle::Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&#name::one()); - let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = #name::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = #name::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * &z; - x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); - z = z.square(); - b *= &z; - v = k; - } - - ::subtle::CtOption::new( - x, - (x * &x).ct_eq(self), // Only return Some if it's the square root. - ) - } - } else { - syn::Error::new_spanned( - &name, - "ff_derive can't generate a square root function for this field.", - ) - .to_compile_error() - }; - - // Compute R^2 mod m - let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); - - let r = biguint_to_u64_vec(r, limbs); - let modulus_repr = endianness.modulus_repr(modulus, limbs * 8); - let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); - - // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(modulus[0]); - } - inv = inv.wrapping_neg(); - - ( - quote! { - /// This is the modulus m of the prime field - const MODULUS: #repr = #repr([#(#modulus_repr,)*]); - - /// This is the modulus m of the prime field in limb form - const MODULUS_LIMBS: #name = #name([#(#modulus,)*]); - - /// The number of bits needed to represent the modulus. - const MODULUS_BITS: u32 = #modulus_num_bits; - - /// The number of bits that must be shaved from the beginning of - /// the representation when randomly sampling. - const REPR_SHAVE_BITS: u32 = #repr_shave_bits; - - /// 2^{limbs*64} mod m - const R: #name = #name(#r); - - /// 2^{limbs*64*2} mod m - const R2: #name = #name(#r2); - - /// -(m^{-1} mod m) mod m - const INV: u64 = #inv; - - /// Multiplicative generator of `MODULUS` - 1 order, also quadratic - /// nonresidue. - const GENERATOR: #name = #name(#generator); - - /// 2^s * t = MODULUS - 1 with t odd - const S: u32 = #s; - - /// 2^s root of unity computed by GENERATOR^t - const ROOT_OF_UNITY: #name = #name(#root_of_unity); - }, - sqrt_impl, - ) -} - -/// Implement PrimeField for the derived type. -fn prime_field_impl( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - endianness: &ReprEndianness, - limbs: usize, - sqrt_impl: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - // Returns r{n} as an ident. - fn get_temp(n: usize) -> syn::Ident { - syn::Ident::new(&format!("r{}", n), proc_macro2::Span::call_site()) - } - - // The parameter list for the mont_reduce() internal method. - // r0: u64, mut r1: u64, mut r2: u64, ... - let mut mont_paramlist = proc_macro2::TokenStream::new(); - mont_paramlist.append_separated( - (0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| { - if i != 0 { - quote! {mut #x: u64} - } else { - quote! {#x: u64} - } - }), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - // Implement montgomery reduction for some number of limbs - fn mont_impl(limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - { - let temp = get_temp(i); - gen.extend(quote! { - let k = #temp.wrapping_mul(INV); - let mut carry = 0; - ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[0], &mut carry); - }); - } - - for j in 1..limbs { - let temp = get_temp(i + j); - gen.extend(quote! { - #temp = ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[#j], &mut carry); - }); - } - - let temp = get_temp(i + limbs); - - if i == 0 { - gen.extend(quote! { - #temp = ::ff::adc(#temp, 0, &mut carry); - }); - } else { - gen.extend(quote! { - #temp = ::ff::adc(#temp, carry2, &mut carry); - }); - } - - if i != (limbs - 1) { - gen.extend(quote! { - let carry2 = carry; - }); - } - } - - for i in 0..limbs { - let temp = get_temp(limbs + i); - - gen.extend(quote! { - self.0[#i] = #temp; - }); - } - - gen - } - - fn sqr_impl(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..(limbs - 1) { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in (i + 1)..limbs { - let temp = get_temp(i + j); - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#j], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #a.0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - for i in 1..(limbs * 2) { - let temp0 = get_temp(limbs * 2 - i); - let temp1 = get_temp(limbs * 2 - i - 1); - - if i == 1 { - gen.extend(quote! { - let #temp0 = #temp1 >> 63; - }); - } else if i == (limbs * 2 - 1) { - gen.extend(quote! { - let #temp0 = #temp0 << 1; - }); - } else { - gen.extend(quote! { - let #temp0 = (#temp0 << 1) | (#temp1 >> 63); - }); - } - } - - gen.extend(quote! { - let mut carry = 0; - }); - - for i in 0..limbs { - let temp0 = get_temp(i * 2); - let temp1 = get_temp(i * 2 + 1); - if i == 0 { - gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#i], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(#temp0, #a.0[#i], #a.0[#i], &mut carry); - }); - } - - gen.extend(quote! { - let #temp1 = ::ff::adc(#temp1, 0, &mut carry); - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - let mut ret = *self; - ret.mont_reduce(#mont_calling); - ret - }); - - gen - } - - fn mul_impl( - a: proc_macro2::TokenStream, - b: proc_macro2::TokenStream, - limbs: usize, - ) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in 0..limbs { - let temp = get_temp(i + j); - - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, #a.0[#i], #b.0[#j], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #b.0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - self.mont_reduce(#mont_calling); - }); - - gen - } - - /// Generates an implementation of multiplicative inversion within the target prime - /// field. - fn inv_impl( - a: proc_macro2::TokenStream, - name: &syn::Ident, - modulus: &BigUint, - ) -> proc_macro2::TokenStream { - // Addition chain for p - 2 - let mod_minus_2 = pow_fixed::generate(&a, modulus - BigUint::from(2u64)); - - quote! { - use ::subtle::ConstantTimeEq; - - // By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then: - // a^-1 ≡ a^(phi(p) - 1) mod p - // - // `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and - // thus: - // a^-1 ≡ a^(p - 2) mod p - let inv = { - #mod_minus_2 - }; - - ::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero())) - } - } - - let squaring_impl = sqr_impl(quote! {self}, limbs); - let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs); - let invert_impl = inv_impl(quote! {self}, name, modulus); - let montgomery_impl = mont_impl(limbs); - - // self.0[0].ct_eq(&other.0[0]) & self.0[1].ct_eq(&other.0[1]) & ... - let mut ct_eq_impl = proc_macro2::TokenStream::new(); - ct_eq_impl.append_separated( - (0..limbs).map(|i| quote! { self.0[#i].ct_eq(&other.0[#i]) }), - proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), - ); - - fn mont_reduce_params(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { - // a.0[0], a.0[1], ..., 0, 0, 0, 0, ... - let mut mont_reduce_params = proc_macro2::TokenStream::new(); - mont_reduce_params.append_separated( - (0..limbs) - .map(|i| quote! { #a.0[#i] }) - .chain((0..limbs).map(|_| quote! {0})), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - mont_reduce_params - } - - let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); - let mont_reduce_other_params = mont_reduce_params(quote! {other}, limbs); - - let repr_endianness = endianness.repr_endianness(); - let from_repr_impl = endianness.from_repr(name, limbs); - let to_repr_impl = endianness.to_repr(repr, &mont_reduce_self_params, limbs); - - let top_limb_index = limbs - 1; - - quote! { - impl ::core::marker::Copy for #name { } - - impl ::core::clone::Clone for #name { - fn clone(&self) -> #name { - *self - } - } - - impl ::core::default::Default for #name { - fn default() -> #name { - #name::zero() - } - } - - impl ::subtle::ConstantTimeEq for #name { - fn ct_eq(&self, other: &#name) -> ::subtle::Choice { - self.to_repr().ct_eq(&other.to_repr()) - } - } - - impl ::core::cmp::PartialEq for #name { - fn eq(&self, other: &#name) -> bool { - use ::subtle::ConstantTimeEq; - self.ct_eq(other).into() - } - } - - impl ::core::cmp::Eq for #name { } - - impl ::core::fmt::Debug for #name - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:?})", stringify!(#name), self.to_repr()) - } - } - - /// Elements are ordered lexicographically. - impl Ord for #name { - #[inline(always)] - fn cmp(&self, other: &#name) -> ::core::cmp::Ordering { - let mut a = *self; - a.mont_reduce( - #mont_reduce_self_params - ); - - let mut b = *other; - b.mont_reduce( - #mont_reduce_other_params - ); - - a.cmp_native(&b) - } - } - - impl PartialOrd for #name { - #[inline(always)] - fn partial_cmp(&self, other: &#name) -> Option<::core::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::core::fmt::Display for #name { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({})", stringify!(#name), self.to_repr()) - } - } - - impl From for #name { - #[inline(always)] - fn from(val: u64) -> #name { - let mut raw = [0u64; #limbs]; - raw[0] = val; - #name(raw) * R2 - } - } - - impl From<#name> for #repr { - fn from(e: #name) -> #repr { - e.to_repr() - } - } - - impl<'a> From<&'a #name> for #repr { - fn from(e: &'a #name) -> #repr { - e.to_repr() - } - } - - impl ::subtle::ConditionallySelectable for #name { - fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name { - let mut res = [0u64; #limbs]; - for i in 0..#limbs { - res[i] = u64::conditional_select(&a.0[i], &b.0[i], choice); - } - #name(res) - } - } - - impl ::core::ops::Neg for #name { - type Output = #name; - - #[inline] - fn neg(self) -> #name { - let mut ret = self; - if !ret.is_zero() { - let mut tmp = MODULUS_LIMBS; - tmp.sub_noborrow(&ret); - ret = tmp; - } - ret - } - } - - impl<'r> ::core::ops::Add<&'r #name> for #name { - type Output = #name; - - #[inline] - fn add(self, other: &#name) -> #name { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::core::ops::Add for #name { - type Output = #name; - - #[inline] - fn add(self, other: #name) -> Self { - self + &other - } - } - - impl<'r> ::core::ops::AddAssign<&'r #name> for #name { - #[inline] - fn add_assign(&mut self, other: &#name) { - // This cannot exceed the backing capacity. - self.add_nocarry(other); - - // However, it may need to be reduced. - self.reduce(); - } - } - - impl ::core::ops::AddAssign for #name { - #[inline] - fn add_assign(&mut self, other: #name) { - self.add_assign(&other); - } - } - - impl<'r> ::core::ops::Sub<&'r #name> for #name { - type Output = #name; - - #[inline] - fn sub(self, other: &#name) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::core::ops::Sub for #name { - type Output = #name; - - #[inline] - fn sub(self, other: #name) -> Self { - self - &other - } - } - - impl<'r> ::core::ops::SubAssign<&'r #name> for #name { - #[inline] - fn sub_assign(&mut self, other: &#name) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.cmp_native(self) == ::core::cmp::Ordering::Greater { - self.add_nocarry(&MODULUS_LIMBS); - } - - self.sub_noborrow(other); - } - } - - impl ::core::ops::SubAssign for #name { - #[inline] - fn sub_assign(&mut self, other: #name) { - self.sub_assign(&other); - } - } - - impl<'r> ::core::ops::Mul<&'r #name> for #name { - type Output = #name; - - #[inline] - fn mul(self, other: &#name) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } - } - - impl ::core::ops::Mul for #name { - type Output = #name; - - #[inline] - fn mul(self, other: #name) -> Self { - self * &other - } - } - - impl<'r> ::core::ops::MulAssign<&'r #name> for #name { - #[inline] - fn mul_assign(&mut self, other: &#name) - { - #multiply_impl - } - } - - impl ::core::ops::MulAssign for #name { - #[inline] - fn mul_assign(&mut self, other: #name) - { - self.mul_assign(&other); - } - } - - impl ::ff::PrimeField for #name { - type Repr = #repr; - type ReprEndianness = #repr_endianness; - - fn from_repr(r: #repr) -> Option<#name> { - #from_repr_impl - } - - fn to_repr(&self) -> #repr { - #to_repr_impl - } - - #[inline(always)] - fn is_odd(&self) -> bool { - let mut r = *self; - r.mont_reduce( - #mont_reduce_self_params - ); - - r.0[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS - } - - const NUM_BITS: u32 = MODULUS_BITS; - - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } - } - - impl ::ff::Field for #name { - /// Computes a uniformly random element using rejection sampling. - fn random(rng: &mut R) -> Self { - loop { - let mut tmp = { - let mut repr = [0u64; #limbs]; - for i in 0..#limbs { - repr[i] = rng.next_u64(); - } - #name(repr) - }; - - // Mask away the unused most-significant bits. - tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; - - if tmp.is_valid() { - return tmp - } - } - } - - #[inline] - fn zero() -> Self { - #name([0; #limbs]) - } - - #[inline] - fn one() -> Self { - R - } - - #[inline] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline] - fn double(&self) -> Self { - let mut ret = *self; - - // This cannot exceed the backing capacity. - let mut last = 0; - for i in &mut ret.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - - // However, it may need to be reduced. - ret.reduce(); - - ret - } - - fn invert(&self) -> ::subtle::CtOption { - #invert_impl - } - - #[inline] - fn square(&self) -> Self - { - #squaring_impl - } - - fn sqrt(&self) -> ::subtle::CtOption { - #sqrt_impl - } - } - - impl #name { - /// Compares two elements in native representation. This is only used - /// internally. - #[inline(always)] - fn cmp_native(&self, other: &#name) -> ::core::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::core::cmp::Ordering::Less - } else if a > b { - return ::core::cmp::Ordering::Greater - } - } - - ::core::cmp::Ordering::Equal - } - - /// Determines if the element is really in the field. This is only used - /// internally. - #[inline(always)] - fn is_valid(&self) -> bool { - // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use - // this internal function to eliminate the cycle. - self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &#name) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &#name) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::sbb(*a, *b, &mut borrow); - } - } - - /// Subtracts the modulus from this element if this element is not in the - /// field. Only used interally. - #[inline(always)] - fn reduce(&mut self) { - if !self.is_valid() { - self.sub_noborrow(&MODULUS_LIMBS); - } - } - - #[inline(always)] - fn mont_reduce( - &mut self, - #mont_paramlist - ) - { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - #montgomery_impl - - self.reduce(); - } - } - } -} diff --git a/ff/ff_derive/src/pow_fixed.rs b/ff/ff_derive/src/pow_fixed.rs deleted file mode 100644 index 1d2b37ad29..0000000000 --- a/ff/ff_derive/src/pow_fixed.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Fixed-exponent variable-base exponentiation using addition chains. - -use addchain::{build_addition_chain, Step}; -use num_bigint::BigUint; -use quote::quote; -use syn::Ident; - -/// Returns t{n} as an ident. -fn get_temp(n: usize) -> Ident { - Ident::new(&format!("t{}", n), proc_macro2::Span::call_site()) -} - -pub(crate) fn generate( - base: &proc_macro2::TokenStream, - exponent: BigUint, -) -> proc_macro2::TokenStream { - let steps = build_addition_chain(exponent); - - let mut gen = proc_macro2::TokenStream::new(); - - // First entry in chain is one, i.e. the base. - let start = get_temp(0); - gen.extend(quote! { - let #start = #base; - }); - - let mut tmps = vec![start]; - for (i, step) in steps.into_iter().enumerate() { - let out = get_temp(i + 1); - - gen.extend(match step { - Step::Double { index } => { - let val = &tmps[index]; - quote! { - let #out = #val.square(); - } - } - Step::Add { left, right } => { - let left = &tmps[left]; - let right = &tmps[right]; - quote! { - let #out = #left * #right; - } - } - }); - - tmps.push(out.clone()); - } - - let end = tmps.last().expect("have last"); - gen.extend(quote! { - #end - }); - - gen -} diff --git a/ff/src/lib.rs b/ff/src/lib.rs deleted file mode 100644 index 9d96f5edeb..0000000000 --- a/ff/src/lib.rs +++ /dev/null @@ -1,343 +0,0 @@ -//! This crate provides traits for working with finite fields. - -// Catch documentation errors caused by code changes. -#![no_std] -#![deny(intra_doc_link_resolution_failure)] -#![allow(unused_imports)] - -#[cfg(feature = "std")] -#[macro_use] -extern crate std; - -#[cfg(feature = "derive")] -pub use ff_derive::*; - -use byteorder::ByteOrder; -use core::convert::TryFrom; -use core::fmt; -use core::marker::PhantomData; -use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; -use rand_core::RngCore; -#[cfg(feature = "std")] -use std::io::{self, Read, Write}; -use subtle::{ConditionallySelectable, CtOption}; - -/// This trait represents an element of a field. -pub trait Field: - Sized - + Eq - + Copy - + Clone - + Default - + Send - + Sync - + fmt::Debug - + fmt::Display - + 'static - + ConditionallySelectable - + Add - + Sub - + Mul - + Neg - + for<'a> Add<&'a Self, Output = Self> - + for<'a> Mul<&'a Self, Output = Self> - + for<'a> Sub<&'a Self, Output = Self> - + MulAssign - + AddAssign - + SubAssign - + for<'a> MulAssign<&'a Self> - + for<'a> AddAssign<&'a Self> - + for<'a> SubAssign<&'a Self> -{ - /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; - - /// Returns the zero element of the field, the additive identity. - fn zero() -> Self; - - /// Returns the one element of the field, the multiplicative identity. - fn one() -> Self; - - /// Returns true iff this element is zero. - fn is_zero(&self) -> bool; - - /// Squares this element. - #[must_use] - fn square(&self) -> Self; - - /// Doubles this element. - #[must_use] - fn double(&self) -> Self; - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption; - - /// Returns the square root of the field element, if it is - /// quadratic residue. - fn sqrt(&self) -> CtOption; - - /// Exponentiates `self` by `exp`, where `exp` is a little-endian order - /// integer exponent. - /// - /// **This operation is variable time with respect to the exponent.** If the - /// exponent is fixed, this operation is effectively constant time. - fn pow_vartime>(&self, exp: S) -> Self { - let mut res = Self::one(); - for e in exp.as_ref().iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res.mul_assign(self); - } - } - } - - res - } -} - -/// Helper trait for converting the binary representation of a prime field element into a -/// specific endianness. This is useful when you need to act on the bit representation -/// of an element generically, as the native binary representation of a prime field is -/// field-dependent. -pub trait Endianness: ByteOrder { - /// Converts the provided representation between native and little-endian. - fn toggle_little_endian>(t: &mut T); -} - -impl Endianness for byteorder::BigEndian { - fn toggle_little_endian>(t: &mut T) { - t.as_mut().reverse(); - } -} - -impl Endianness for byteorder::LittleEndian { - fn toggle_little_endian>(_: &mut T) { - // No-op - } -} - -/// This represents an element of a prime field. -pub trait PrimeField: Field + From { - /// The prime field can be converted back and forth into this binary - /// representation. - type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + for<'r> From<&'r Self>; - - /// This indicates the endianness of [`PrimeField::Repr`]. - type ReprEndianness: Endianness; - - /// Interpret a string of numbers as a (congruent) prime field element. - /// Does not accept unnecessary leading zeroes or a blank string. - fn from_str(s: &str) -> Option { - if s.is_empty() { - return None; - } - - if s == "0" { - return Some(Self::zero()); - } - - let mut res = Self::zero(); - - let ten = Self::from(10); - - let mut first_digit = true; - - for c in s.chars() { - match c.to_digit(10) { - Some(c) => { - if first_digit { - if c == 0 { - return None; - } - - first_digit = false; - } - - res.mul_assign(&ten); - res.add_assign(&Self::from(u64::from(c))); - } - None => { - return None; - } - } - } - - Some(res) - } - - /// Attempts to convert a byte representation of a field element into an element of - /// this prime field, failing if the input is not canonical (is not smaller than the - /// field's modulus). - /// - /// The byte representation is interpreted with the endianness defined by - /// [`PrimeField::ReprEndianness`]. - fn from_repr(_: Self::Repr) -> Option; - - /// Converts an element of the prime field into the standard byte representation for - /// this field. - /// - /// The endianness of the byte representation is defined by - /// [`PrimeField::ReprEndianness`]. - fn to_repr(&self) -> Self::Repr; - - /// Returns true iff this element is odd. - fn is_odd(&self) -> bool; - - /// Returns true iff this element is even. - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - /// Returns the field characteristic; the modulus. - fn char() -> Self::Repr; - - /// How many bits are needed to represent an element of this field. - const NUM_BITS: u32; - - /// How many bits of information can be reliably stored in the field element. - const CAPACITY: u32; - - /// Returns the multiplicative generator of `char()` - 1 order. This element - /// must also be quadratic nonresidue. - fn multiplicative_generator() -> Self; - - /// 2^s * t = `char()` - 1 with t odd. - const S: u32; - - /// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()` - /// by t. - fn root_of_unity() -> Self; -} - -/// Takes a little-endian representation of some value, and returns its bits in big-endian -/// order. -#[derive(Debug)] -pub struct BitIterator> { - t: E, - n: usize, - _limb: PhantomData, -} - -impl> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 64; - - BitIterator { - t, - n, - _limb: PhantomData::default(), - } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - if self.n == 0 { - None - } else { - self.n -= 1; - let part = self.n / 64; - let bit = self.n - (64 * part); - - Some(self.t.as_ref()[part] & (1 << bit) > 0) - } - } -} - -impl> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 8; - - BitIterator { - t, - n, - _limb: PhantomData::default(), - } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - if self.n == 0 { - None - } else { - self.n -= 1; - let part = self.n / 8; - let bit = self.n - (8 * part); - - Some(self.t.as_ref()[part] & (1 << bit) > 0) - } - } -} - -#[test] -fn test_bit_iterator() { - let mut a = BitIterator::::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]); - let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001"; - - for e in expected.chars() { - assert!(a.next().unwrap() == (e == '1')); - } - - assert!(a.next().is_none()); - - let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001"; - - let mut a = BitIterator::::new([ - 0x429d_5f3a_c3a3_b759, - 0xb10f_4c66_768b_1c92, - 0x9236_8b6d_16ec_d3b4, - 0xa57e_a85a_e877_5219, - ]); - - for e in expected.chars() { - assert!(a.next().unwrap() == (e == '1')); - } - - assert!(a.next().is_none()); -} - -pub use self::arith_impl::*; - -mod arith_impl { - /// Calculate a - b - borrow, returning the result and modifying - /// the borrow value. - #[inline(always)] - pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { - let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow); - - *borrow = if tmp >> 64 == 0 { 1 } else { 0 }; - - tmp as u64 - } - - /// Calculate a + b + carry, returning the sum and modifying the - /// carry value. - #[inline(always)] - pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { - let tmp = u128::from(a) + u128::from(b) + u128::from(*carry); - - *carry = (tmp >> 64) as u64; - - tmp as u64 - } - - /// Calculate a + (b * c) + carry, returning the least significant digit - /// and setting carry to the most significant digit. - #[inline(always)] - pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { - let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry); - - *carry = (tmp >> 64) as u64; - - tmp as u64 - } -} diff --git a/group/.gitignore b/group/.gitignore deleted file mode 100644 index 693699042b..0000000000 --- a/group/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/group/COPYRIGHT b/group/COPYRIGHT deleted file mode 100644 index 849e327475..0000000000 --- a/group/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "group" library are retained by their contributors. No -copyright assignment is required to contribute to the "group" library. - -The "group" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/group/Cargo.toml b/group/Cargo.toml deleted file mode 100644 index 57ad76f81d..0000000000 --- a/group/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "group" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -readme = "README.md" -license = "MIT/Apache-2.0" - -description = "Elliptic curve group traits and utilities" -documentation = "https://docs.rs/group/" -homepage = "https://github.com/ebfull/group" -repository = "https://github.com/ebfull/group" -edition = "2018" - -[dependencies] -byteorder = { version = "1", default-features = false } -ff = { version = "0.7", path = "../ff" } -rand = "0.7" -rand_xorshift = "0.2" -subtle = { version = "2.2.1", default-features = false } - -[badges] -maintenance = { status = "actively-developed" } diff --git a/group/LICENSE-APACHE b/group/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/group/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/group/LICENSE-MIT b/group/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/group/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/group/README.md b/group/README.md deleted file mode 100644 index 5c2398b2b2..0000000000 --- a/group/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # - -`group` is a crate for working with groups over elliptic curves. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs deleted file mode 100644 index 4722909350..0000000000 --- a/group/src/cofactor.rs +++ /dev/null @@ -1,119 +0,0 @@ -use core::fmt; -use core::ops::{Mul, Neg}; -use ff::{BitIterator, Endianness, PrimeField}; -use subtle::{Choice, CtOption}; - -use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned}; - -/// This trait represents an element of a cryptographic group with a large prime-order -/// subgroup and a comparatively-small cofactor. -pub trait CofactorGroup: - Group - + GroupEncoding - + GroupOps<::Subgroup> - + GroupOpsOwned<::Subgroup> -{ - /// The large prime-order subgroup in which cryptographic operations are performed. - /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. - type Subgroup: PrimeGroup + Into; - - /// Maps `self` to the prime-order subgroup by multiplying this element by some - /// `k`-multiple of the cofactor. - /// - /// The value `k` does not vary between inputs for a given implementation, but may - /// vary between different implementations of `CofactorGroup` because some groups have - /// more efficient methods of clearing the cofactor when `k` is allowed to be - /// different than `1`. - /// - /// If `Self` implements [`PrimeGroup`], this returns `self`. - fn clear_cofactor(&self) -> Self::Subgroup; - - /// Returns `self` if it is contained in the prime-order subgroup. - /// - /// If `Self` implements [`PrimeGroup`], this returns `Some(self)`. - fn into_subgroup(self) -> CtOption; - - /// Determines if this element is of small order. - /// - /// Returns: - /// - `true` if `self` is in the torsion subgroup. - /// - `false` if `self` is not in the torsion subgroup. - fn is_small_order(&self) -> Choice { - self.clear_cofactor().is_identity() - } - - /// Determines if this element is "torsion free", i.e., is contained in the - /// prime-order subgroup. - /// - /// Returns: - /// - `true` if `self` has zero torsion component and is in the prime-order subgroup. - /// - `false` if `self` has non-zero torsion component and is not in the prime-order - /// subgroup. - fn is_torsion_free(&self) -> Choice { - // Obtain the scalar field characteristic in little endian. - let mut char = Self::Scalar::char(); - ::ReprEndianness::toggle_little_endian(&mut char); - - // Multiply self by the characteristic to eliminate any prime-order subgroup - // component. - let bits = BitIterator::::new(char); - let mut res = Self::identity(); - for i in bits { - res = res.double(); - if i { - res.add_assign(self) - } - } - - // If the result is the identity, there was zero torsion component! - res.is_identity() - } -} - -/// Efficient representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CofactorCurve: - Curve::Affine> + CofactorGroup -{ - type Affine: CofactorCurveAffine - + Mul - + for<'r> Mul; -} - -/// Affine representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CofactorCurveAffine: - GroupEncoding - + Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + fmt::Display - + PartialEq - + Eq - + 'static - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul< - ::Scalar, - Output = ::Curve, - > -{ - type Scalar: PrimeField; - type Curve: CofactorCurve; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; - - /// Converts this element to its curve representation. - fn to_curve(&self) -> Self::Curve; -} diff --git a/group/src/lib.rs b/group/src/lib.rs deleted file mode 100644 index cae3b81cf2..0000000000 --- a/group/src/lib.rs +++ /dev/null @@ -1,164 +0,0 @@ -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] - -use ff::PrimeField; -use rand::RngCore; -use std::fmt; -use std::iter::Sum; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, CtOption}; - -pub mod cofactor; -pub mod prime; -pub mod tests; - -mod wnaf; -pub use self::wnaf::{Wnaf, WnafGroup}; - -/// A helper trait for types with a group operation. -pub trait GroupOps: - Add + Sub + AddAssign + SubAssign -{ -} - -impl GroupOps for T where - T: Add + Sub + AddAssign + SubAssign -{ -} - -/// A helper trait for references with a group operation. -pub trait GroupOpsOwned: for<'r> GroupOps<&'r Rhs, Output> {} -impl GroupOpsOwned for T where T: for<'r> GroupOps<&'r Rhs, Output> {} - -/// A helper trait for types implementing group scalar multiplication. -pub trait ScalarMul: Mul + MulAssign {} - -impl ScalarMul for T where T: Mul + MulAssign -{} - -/// A helper trait for references implementing group scalar multiplication. -pub trait ScalarMulOwned: for<'r> ScalarMul<&'r Rhs, Output> {} -impl ScalarMulOwned for T where T: for<'r> ScalarMul<&'r Rhs, Output> {} - -/// This trait represents an element of a cryptographic group. -pub trait Group: - Clone - + Copy - + fmt::Debug - + fmt::Display - + Eq - + Sized - + Send - + Sync - + 'static - + Sum - + for<'a> Sum<&'a Self> - + Neg - + GroupOps - + GroupOpsOwned - + ScalarMul<::Scalar> - + ScalarMulOwned<::Scalar> -{ - /// Scalars modulo the order of this group's scalar field. - type Scalar: PrimeField; - - /// Returns an element chosen uniformly at random from the non-identity elements of - /// this group. - /// - /// This function is non-deterministic, and samples from the user-provided RNG. - fn random(rng: &mut R) -> Self; - - /// Returns the additive identity, also known as the "neutral element". - fn identity() -> Self; - - /// Returns a fixed generator of the prime-order subgroup. - fn generator() -> Self; - - /// Determines if this point is the identity. - fn is_identity(&self) -> Choice; - - /// Doubles this element. - #[must_use] - fn double(&self) -> Self; -} - -/// Efficient representation of an elliptic curve point guaranteed. -pub trait Curve: - Group + GroupOps<::AffineRepr> + GroupOpsOwned<::AffineRepr> -{ - /// The affine representation for this elliptic curve. - type AffineRepr; - - /// Converts a batch of projective elements into affine elements. This function will - /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - assert_eq!(p.len(), q.len()); - - for (p, q) in p.iter().zip(q.iter_mut()) { - *q = p.to_affine(); - } - } - - /// Converts this element into its affine representation. - fn to_affine(&self) -> Self::AffineRepr; -} - -pub trait GroupEncoding: Sized { - /// The encoding of group elements. - /// - /// The `Default` implementation is not required to return a valid point encoding. The - /// bound is present to enable encodings to be constructed generically: - /// ``` - /// # use group::GroupEncoding; - /// # use subtle::CtOption; - /// # struct G; - /// # impl GroupEncoding for G { - /// # type Repr = [u8; 0]; - /// # fn from_bytes(bytes: &Self::Repr) -> CtOption { unimplemented!() } - /// # fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { unimplemented!() } - /// # fn to_bytes(&self) -> Self::Repr { unimplemented!() } - /// # } - /// # let buf = &[0u8; 0][..]; - /// let mut encoding = ::Repr::default(); - /// encoding.as_mut().copy_from_slice(buf); - /// ``` - /// - /// It is recommended that the default should be the all-zeroes encoding. - type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; - - /// Attempts to deserialize a group element from its encoding. - fn from_bytes(bytes: &Self::Repr) -> CtOption; - - /// Attempts to deserialize a group element, not checking if the element is valid. - /// - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using - /// [`GroupEncoding::from_bytes`] instead. - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption; - - /// Converts this element into its byte encoding. This may or may not support - /// encoding the identity. - // TODO: Figure out how to handle identity encoding generically. - fn to_bytes(&self) -> Self::Repr; -} - -/// Affine representation of a point on an elliptic curve that has a defined uncompressed -/// encoding. -pub trait UncompressedEncoding: Sized { - type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; - - /// Attempts to deserialize an element from its uncompressed encoding. - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; - - /// Attempts to deserialize an uncompressed element, not checking if the element is in - /// the correct subgroup. - /// - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using - /// [`UncompressedEncoding::from_uncompressed`] instead. - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption; - - /// Converts this element into its uncompressed encoding, so long as it's not - /// the point at infinity. - fn to_uncompressed(&self) -> Self::Uncompressed; -} diff --git a/group/src/prime.rs b/group/src/prime.rs deleted file mode 100644 index d02105d794..0000000000 --- a/group/src/prime.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::fmt; -use core::ops::{Mul, Neg}; -use ff::PrimeField; -use subtle::Choice; - -use crate::{Curve, Group, GroupEncoding}; - -/// This trait represents an element of a prime-order cryptographic group. -pub trait PrimeGroup: Group + GroupEncoding {} - -/// Efficient representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait PrimeCurve: Curve::Affine> + PrimeGroup { - type Affine: PrimeCurveAffine - + Mul - + for<'r> Mul; -} - -/// Affine representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait PrimeCurveAffine: - GroupEncoding - + Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + fmt::Display - + PartialEq - + Eq - + 'static - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul<::Scalar, Output = ::Curve> -{ - type Scalar: PrimeField; - type Curve: PrimeCurve; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; - - /// Converts this element to its curve representation. - fn to_curve(&self) -> Self::Curve; -} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs deleted file mode 100644 index 0be5e56af3..0000000000 --- a/group/src/tests/mod.rs +++ /dev/null @@ -1,447 +0,0 @@ -use ff::{Field, PrimeField}; -use rand::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{Mul, Neg}; - -use crate::{ - prime::{PrimeCurve, PrimeCurveAffine}, - wnaf::WnafGroup, - GroupEncoding, UncompressedEncoding, -}; - -pub fn curve_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // Negation edge case with identity. - { - let z = G::identity().neg(); - assert!(bool::from(z.is_identity())); - } - - // Doubling edge case with identity. - { - let z = G::identity().double(); - assert!(bool::from(z.is_identity())); - } - - // Addition edge cases with identity - { - let mut r = G::random(&mut rng); - let rcopy = r; - r.add_assign(&G::identity()); - assert_eq!(r, rcopy); - r.add_assign(&G::Affine::identity()); - assert_eq!(r, rcopy); - - let mut z = G::identity(); - z.add_assign(&G::identity()); - assert!(bool::from(z.is_identity())); - z.add_assign(&G::Affine::identity()); - assert!(bool::from(z.is_identity())); - - let mut z2 = z; - z2.add_assign(&r); - - z.add_assign(&r.to_affine()); - - assert_eq!(z, z2); - assert_eq!(z, r); - } - - // Transformations - { - let a = G::random(&mut rng); - let b = a.to_affine().to_curve(); - let c = a.to_affine().to_curve().to_affine().to_curve(); - assert_eq!(a, b); - assert_eq!(b, c); - } - - random_addition_tests::(); - random_multiplication_tests::(); - random_doubling_tests::(); - random_negation_tests::(); - random_transformation_tests::(); - random_compressed_encoding_tests::(); -} - -pub fn random_wnaf_tests() { - use crate::wnaf::*; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - { - let mut table = vec![]; - let mut wnaf = vec![]; - - for w in 2..14 { - for _ in 0..100 { - let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng); - let mut g1 = g; - g1.mul_assign(s); - - wnaf_table(&mut table, g, w); - wnaf_form(&mut wnaf, s.to_repr(), w); - let g2 = wnaf_exp(&table, &wnaf); - - assert_eq!(g1, g2); - } - } - } - - { - fn only_compiles_if_send(_: &S) {} - - for _ in 0..100 { - let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng); - let mut g1 = g; - g1.mul_assign(s); - - let g2 = { - let mut wnaf = Wnaf::new(); - wnaf.base(g, 1).scalar(&s) - }; - let g3 = { - let mut wnaf = Wnaf::new(); - wnaf.scalar(&s).base(g) - }; - let g4 = { - let mut wnaf = Wnaf::new(); - let mut shared = wnaf.base(g, 1).shared(); - - only_compiles_if_send(&shared); - - shared.scalar(&s) - }; - let g5 = { - let mut wnaf = Wnaf::new(); - let mut shared = wnaf.scalar(&s).shared(); - - only_compiles_if_send(&shared); - - shared.base(g) - }; - - let g6 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(&G::Scalar::random(&mut rng)); - } - wnaf.base(g, 1).scalar(&s) - }; - let g7 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(&G::Scalar::random(&mut rng)); - } - wnaf.scalar(&s).base(g) - }; - let g8 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(&G::Scalar::random(&mut rng)); - } - let mut shared = wnaf.base(g, 1).shared(); - - only_compiles_if_send(&shared); - - shared.scalar(&s) - }; - let g9 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(&G::Scalar::random(&mut rng)); - } - let mut shared = wnaf.scalar(&s).shared(); - - only_compiles_if_send(&shared); - - shared.base(g) - }; - - assert_eq!(g1, g2); - assert_eq!(g1, g3); - assert_eq!(g1, g4); - assert_eq!(g1, g5); - assert_eq!(g1, g6); - assert_eq!(g1, g7); - assert_eq!(g1, g8); - assert_eq!(g1, g9); - } - } -} - -fn random_negation_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let r = G::random(&mut rng); - - let s = G::Scalar::random(&mut rng); - let sneg = s.neg(); - - let mut t1 = r; - t1.mul_assign(s); - - let mut t2 = r; - t2.mul_assign(sneg); - - let mut t3 = t1; - t3.add_assign(&t2); - assert!(bool::from(t3.is_identity())); - - let mut t4 = t1; - t4.add_assign(&t2.to_affine()); - assert!(bool::from(t4.is_identity())); - - assert_eq!(t1.neg(), t2); - } -} - -fn random_doubling_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let mut a = G::random(&mut rng); - let mut b = G::random(&mut rng); - - // 2(a + b) - let tmp1 = (a + b).double(); - - // 2a + 2b - a = a.double(); - b = b.double(); - - let mut tmp2 = a; - tmp2.add_assign(&b); - - let mut tmp3 = a; - tmp3.add_assign(&b.to_affine()); - - assert_eq!(tmp1, tmp2); - assert_eq!(tmp1, tmp3); - } -} - -fn random_multiplication_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let mut a = G::random(&mut rng); - let mut b = G::random(&mut rng); - let a_affine = a.to_affine(); - let b_affine = b.to_affine(); - - let s = G::Scalar::random(&mut rng); - - // s ( a + b ) - let mut tmp1 = a; - tmp1.add_assign(&b); - tmp1.mul_assign(s); - - // sa + sb - a.mul_assign(s); - b.mul_assign(s); - - let mut tmp2 = a; - tmp2.add_assign(&b); - - // Affine multiplication - let mut tmp3 = Mul::::mul(a_affine, s); - tmp3.add_assign(Mul::::mul(b_affine, s)); - - assert_eq!(tmp1, tmp2); - assert_eq!(tmp1, tmp3); - } -} - -fn random_addition_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = G::random(&mut rng); - let b = G::random(&mut rng); - let c = G::random(&mut rng); - let a_affine = a.to_affine(); - let b_affine = b.to_affine(); - let c_affine = c.to_affine(); - - // a + a should equal the doubling - { - let mut aplusa = a; - aplusa.add_assign(&a); - - let mut aplusamixed = a; - aplusamixed.add_assign(&a.to_affine()); - - let adouble = a.double(); - - assert_eq!(aplusa, adouble); - assert_eq!(aplusa, aplusamixed); - } - - let mut tmp = vec![G::identity(); 6]; - - // (a + b) + c - tmp[0] = a; - tmp[0].add_assign(&b); - tmp[0].add_assign(&c); - - // a + (b + c) - tmp[1] = b; - tmp[1].add_assign(&c); - tmp[1].add_assign(&a); - - // (a + c) + b - tmp[2] = a; - tmp[2].add_assign(&c); - tmp[2].add_assign(&b); - - // Mixed addition - - // (a + b) + c - tmp[3] = a_affine.to_curve(); - tmp[3].add_assign(&b_affine); - tmp[3].add_assign(&c_affine); - - // a + (b + c) - tmp[4] = b_affine.to_curve(); - tmp[4].add_assign(&c_affine); - tmp[4].add_assign(&a_affine); - - // (a + c) + b - tmp[5] = a_affine.to_curve(); - tmp[5].add_assign(&c_affine); - tmp[5].add_assign(&b_affine); - - // Comparisons - for i in 0..6 { - for j in 0..6 { - assert_eq!(tmp[i], tmp[j]); - assert_eq!(tmp[i].to_affine(), tmp[j].to_affine()); - } - - assert!(tmp[i] != a); - assert!(tmp[i] != b); - assert!(tmp[i] != c); - - assert!(a != tmp[i]); - assert!(b != tmp[i]); - assert!(c != tmp[i]); - } - } -} - -fn random_transformation_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let g = G::random(&mut rng); - let g_affine = g.to_affine(); - let g_projective = g_affine.to_curve(); - assert_eq!(g, g_projective); - } - - // Batch normalization - for _ in 0..10 { - let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::>(); - - use rand::distributions::{Distribution, Uniform}; - let between = Uniform::new(0, 1000); - // Sprinkle in some normalized points - for _ in 0..5 { - v[between.sample(&mut rng)] = G::identity(); - } - for _ in 0..5 { - let s = between.sample(&mut rng); - v[s] = v[s].to_affine().to_curve(); - } - - let expected_v = v.iter().map(|v| v.to_affine()).collect::>(); - - let mut normalized = vec![G::Affine::identity(); v.len()]; - G::batch_normalize(&v, &mut normalized); - - assert_eq!(normalized, expected_v); - } -} - -fn random_compressed_encoding_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!( - G::Affine::from_bytes(&G::Affine::identity().to_bytes()).unwrap(), - G::Affine::identity() - ); - - for _ in 0..1000 { - let mut r = G::random(&mut rng).to_affine(); - - let compressed = r.to_bytes(); - let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); - assert_eq!(de_compressed, r); - - r = r.neg(); - - let compressed = r.to_bytes(); - let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); - assert_eq!(de_compressed, r); - } -} - -pub fn random_uncompressed_encoding_tests() -where - ::Affine: UncompressedEncoding, -{ - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!( - G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), - G::Affine::identity() - ); - - for _ in 0..1000 { - let r = G::random(&mut rng).to_affine(); - - let uncompressed = r.to_uncompressed(); - let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); - assert_eq!(de_uncompressed, r); - } -} diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs deleted file mode 100644 index e81adea2d3..0000000000 --- a/group/src/wnaf.rs +++ /dev/null @@ -1,214 +0,0 @@ -use byteorder::{ByteOrder, LittleEndian}; -use ff::PrimeField; -use std::iter; - -use super::Group; - -/// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`]. -pub trait WnafGroup: Group { - /// Recommends a wNAF window size given the number of scalars you intend to multiply - /// a base by. Always returns a number between 2 and 22, inclusive. - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; -} - -/// Replaces the contents of `table` with a w-NAF window table for the given window size. -pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { - table.truncate(0); - table.reserve(1 << (window - 1)); - - let dbl = base.double(); - - for _ in 0..(1 << (window - 1)) { - table.push(base); - base.add_assign(&dbl); - } -} - -/// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian -/// scalar. -pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { - // Required by the NAF definition - debug_assert!(window >= 2); - // Required so that the NAF digits fit in i64 - debug_assert!(window <= 64); - - wnaf.truncate(0); - - let bit_len = c.as_ref().len() * 8; - let u64_len = (bit_len + 1) / 64; - - let mut c_u64 = vec![0u64; u64_len + 1]; - LittleEndian::read_u64_into(c.as_ref(), &mut c_u64[0..u64_len]); - - let width = 1u64 << window; - let window_mask = width - 1; - - let mut pos = 0; - let mut carry = 0; - while pos < bit_len { - // Construct a buffer of bits of the scalar, starting at bit `pos` - let u64_idx = pos / 64; - let bit_idx = pos % 64; - let bit_buf = if bit_idx + window < 64 { - // This window's bits are contained in a single u64 - c_u64[u64_idx] >> bit_idx - } else { - // Combine the current u64's bits with the bits from the next u64 - (c_u64[u64_idx] >> bit_idx) | (c_u64[u64_idx + 1] << (64 - bit_idx)) - }; - - // Add the carry into the current window - let window_val = carry + (bit_buf & window_mask); - - if window_val & 1 == 0 { - // If the window value is even, preserve the carry and emit 0. - // Why is the carry preserved? - // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 - // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 - wnaf.push(0); - pos += 1; - } else { - wnaf.push(if window_val < width / 2 { - carry = 0; - window_val as i64 - } else { - carry = 1; - (window_val as i64).wrapping_sub(width as i64) - }); - wnaf.extend(iter::repeat(0).take(window - 1)); - pos += window; - } - } -} - -/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. -/// -/// This function must be provided a `table` and `wnaf` that were constructed with -/// the same window size; otherwise, it may panic or produce invalid results. -pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { - let mut result = G::identity(); - - let mut found_one = false; - - for n in wnaf.iter().rev() { - if found_one { - result = result.double(); - } - - if *n != 0 { - found_one = true; - - if *n > 0 { - result += &table[(n / 2) as usize]; - } else { - result -= &table[((-n) / 2) as usize]; - } - } - } - - result -} - -/// A "w-ary non-adjacent form" exponentiation context. -#[derive(Debug)] -pub struct Wnaf { - base: B, - scalar: S, - window_size: W, -} - -impl Wnaf<(), Vec, Vec> { - /// Construct a new wNAF context without allocating. - pub fn new() -> Self { - Wnaf { - base: vec![], - scalar: vec![], - window_size: (), - } - } -} - -impl Wnaf<(), Vec, Vec> { - /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that - /// can perform exponentiations with `.scalar(..)`. - pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf> { - // Compute the appropriate window size based on the number of scalars. - let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); - - // Compute a wNAF table for the provided base and window size. - wnaf_table(&mut self.base, base, window_size); - - // Return a Wnaf object that immutably borrows the computed base storage location, - // but mutably borrows the scalar storage location. - Wnaf { - base: &self.base[..], - scalar: &mut self.scalar, - window_size, - } - } - - /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform - /// exponentiations with `.base(..)`. - pub fn scalar(&mut self, scalar: &::Scalar) -> Wnaf, &[i64]> { - // We hard-code a window size of 4. - let window_size = 4; - - // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); - - // Return a Wnaf object that mutably borrows the base storage location, but - // immutably borrows the computed wNAF form scalar location. - Wnaf { - base: &mut self.base, - scalar: &self.scalar[..], - window_size, - } - } -} - -impl<'a, G: Group> Wnaf> { - /// Constructs new space for the scalar representation while borrowing - /// the computed window table, for sending the window table across threads. - pub fn shared(&self) -> Wnaf> { - Wnaf { - base: self.base, - scalar: vec![], - window_size: self.window_size, - } - } -} - -impl<'a, G: Group> Wnaf, &'a [i64]> { - /// Constructs new space for the window table while borrowing - /// the computed scalar representation, for sending the scalar representation - /// across threads. - pub fn shared(&self) -> Wnaf, &'a [i64]> { - Wnaf { - base: vec![], - scalar: self.scalar, - window_size: self.window_size, - } - } -} - -impl> Wnaf { - /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G - where - B: AsMut>, - { - wnaf_table(self.base.as_mut(), base, self.window_size); - wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) - } -} - -impl>> Wnaf { - /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G - where - B: AsRef<[G]>, - { - wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); - wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) - } -} diff --git a/jubjub/.github/workflows/ci.yml b/jubjub/.github/workflows/ci.yml deleted file mode 100644 index 5d0efb3a21..0000000000 --- a/jubjub/.github/workflows/ci.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: CI checks - -on: [push, pull_request] - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - # Ensure all code has been formatted with rustfmt - - run: rustup component add rustfmt - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check --color always - - test: - name: Test on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build tests - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --release --tests - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release - - no-std: - name: Check no-std compatibility - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - run: rustup target add thumbv6m-none-eabi - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --target thumbv6m-none-eabi --no-default-features - - doc-links: - name: Nightly lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - # Ensure intra-documentation links all resolve correctly - # Requires #![deny(intra_doc_link_resolution_failure)] in crate. - - name: Check intra-doc links - uses: actions-rs/cargo@v1 - with: - command: doc - args: --document-private-items diff --git a/jubjub/.gitignore b/jubjub/.gitignore deleted file mode 100644 index 693699042b..0000000000 --- a/jubjub/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/jubjub/COPYRIGHT b/jubjub/COPYRIGHT deleted file mode 100644 index aaca1cc674..0000000000 --- a/jubjub/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "jubjub" library are retained by their contributors. No -copyright assignment is required to contribute to the "jubjub" library. - -The "jubjub" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml deleted file mode 100644 index 3f9485ca95..0000000000 --- a/jubjub/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -authors = [ - "Sean Bowe ", - "Eirik Ogilvie-Wigley ", - "Jack Grigg ", -] -description = "Implementation of the Jubjub elliptic curve group" -documentation = "https://docs.rs/jubjub/" -homepage = "https://github.com/zkcrypto/jubjub" -license = "MIT/Apache-2.0" -name = "jubjub" -repository = "https://github.com/zkcrypto/jubjub" -version = "0.4.0" -edition = "2018" - -[dependencies.bls12_381] -path = "../bls12_381" -version = "0.2" -default-features = false - -[dependencies.byteorder] -version = "1" -default-features = false - -[dependencies.ff] -path = "../ff" -version = "0.7" -default-features = false - -[dependencies.group] -path = "../group" -version = "0.7" -default-features = false - -[dependencies.rand_core] -version = "0.5" -default-features = false - -[dependencies.subtle] -version = "^2.2.1" -default-features = false - -[dev-dependencies] -criterion = "0.3" - -[dev-dependencies.rand_xorshift] -version = "0.2" -default-features = false - -[features] -default = [] - -[[bench]] -name = "fq_bench" -harness = false - -[[bench]] -name = "fr_bench" -harness = false - -[[bench]] -name = "point_bench" -harness = false diff --git a/jubjub/LICENSE-APACHE b/jubjub/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/jubjub/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/jubjub/LICENSE-MIT b/jubjub/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/jubjub/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/jubjub/README.md b/jubjub/README.md deleted file mode 100644 index da5bd530ad..0000000000 --- a/jubjub/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) # - - - -This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields. - -* **This implementation has not been reviewed or audited. Use at your own risk.** -* This implementation targets Rust `1.36` or later. -* All operations are constant time unless explicitly noted. -* This implementation does not require the Rust standard library. - -## [Documentation](https://docs.rs/jubjub) - -## Curve Description - -Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`. - -``` -q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 -d = -(10240/10241) -``` - -The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction. - -Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519. - -Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity. - -## Acknowledgements - -Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image. - -Please see `Cargo.toml` for a list of primary authors of this codebase. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/jubjub/RELEASES.md b/jubjub/RELEASES.md deleted file mode 100644 index b9a1e9efff..0000000000 --- a/jubjub/RELEASES.md +++ /dev/null @@ -1,53 +0,0 @@ -# 0.4.0 - -This release adds implementations of the `ff` and `group` traits. Additional trait -implementations (for standard traits) have been added where the `ff` and `group` trait -bounds require them. - -## Added -* `jubjub::SubgroupPoint`, which represents an element of Jubjub's prime-order subgroup. - It implements the following traits: - * `group::{Group, GroupEncoding}` - * `group::prime::PrimeGroup` -* New trait implementations for `jubjub::ExtendedPoint`: - * `group::{Curve, Group, GroupEncoding, WnafGroup}` - * `group::cofactor::{CofactorCurve, CofactorGroup}` -* New trait implementations for `jubjub::AffinePoint`: - * `group::GroupEncoding` - * `group::cofactor::CofactorCurveAffine` -* New trait implementations for `jubjub::Fr`: - * `ff::{Field, PrimeField}` -* `jubjub::AffinePoint::is_identity` -* `jubjub::AffinePoint::to_extended` -* `jubjub::Scalar`, as an alias for `jubjub::Fr`. - -## Changed -* We've migrated to `bls12_381 0.2`. -* `rand_core` is now a regular dependency. -* We depend on the `byteorder` crate again, as it is part of the `ff::PrimeField` trait. -* The benchmarks are now implemented using `criterion`. - -# 0.3.0 - -This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export. - -* The `Fq` and `Fr` field types now have better constant function support for various operations and constructors. -* We no longer depend on the `byteorder` crate. -* We've bumped our `rand_core` dev-dependency up to 0.5. -* We've removed the `std` and `nightly` features. -* We've bumped our dependency of `subtle` up to `^2.2.1`. - -# 0.2.0 - -This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes. - -* Implemented `Mul` for `AffineNielsPoint` and `ExtendedNielsPoint` -* Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics. -* Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint` -* Removed `CtOption` and replaced it with `CtOption` from `subtle` crate. -* Modified receivers of some methods to reduce stack usage -* Changed various `into_bytes` methods into `to_bytes` - -# 0.1.0 - -Initial release. diff --git a/jubjub/benches/fq_bench.rs b/jubjub/benches/fq_bench.rs deleted file mode 100644 index 65eceafc84..0000000000 --- a/jubjub/benches/fq_bench.rs +++ /dev/null @@ -1,58 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -fn bench_add_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq add_assign", |b| { - b.iter(move || { - n += &neg_one; - }) - }); -} - -fn bench_sub_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq sub_assign", |b| { - b.iter(move || { - n -= &neg_one; - }) - }); -} - -fn bench_mul_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq mul_assign", |b| { - b.iter(move || { - n *= &neg_one; - }) - }); -} - -fn bench_square(c: &mut Criterion) { - let n = Fq::one(); - c.bench_function("Fq square", |b| b.iter(move || n.square())); -} - -fn bench_invert(c: &mut Criterion) { - let n = Fq::one(); - c.bench_function("Fq invert", |b| b.iter(move || n.invert())); -} - -fn bench_sqrt(c: &mut Criterion) { - let n = Fq::one().double().double(); - c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt())); -} - -criterion_group!( - benches, - bench_add_assign, - bench_sub_assign, - bench_mul_assign, - bench_square, - bench_invert, - bench_sqrt, -); -criterion_main!(benches); diff --git a/jubjub/benches/fr_bench.rs b/jubjub/benches/fr_bench.rs deleted file mode 100644 index 8dc9ce2216..0000000000 --- a/jubjub/benches/fr_bench.rs +++ /dev/null @@ -1,58 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -fn bench_add_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr add_assign", |b| { - b.iter(move || { - n += &neg_one; - }) - }); -} - -fn bench_sub_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr sub_assign", |b| { - b.iter(move || { - n -= &neg_one; - }) - }); -} - -fn bench_mul_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr mul_assign", |b| { - b.iter(move || { - n *= &neg_one; - }) - }); -} - -fn bench_square(c: &mut Criterion) { - let n = Fr::one(); - c.bench_function("Fr square", |b| b.iter(move || n.square())); -} - -fn bench_invert(c: &mut Criterion) { - let n = Fr::one(); - c.bench_function("Fr invert", |b| b.iter(move || n.invert())); -} - -fn bench_sqrt(c: &mut Criterion) { - let n = Fr::one().double().double(); - c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt())); -} - -criterion_group!( - benches, - bench_add_assign, - bench_sub_assign, - bench_mul_assign, - bench_square, - bench_invert, - bench_sqrt, -); -criterion_main!(benches); diff --git a/jubjub/benches/point_bench.rs b/jubjub/benches/point_bench.rs deleted file mode 100644 index 1659ea5f97..0000000000 --- a/jubjub/benches/point_bench.rs +++ /dev/null @@ -1,73 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -// Non-Niels - -fn bench_point_doubling(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - c.bench_function("Jubjub point doubling", |bencher| { - bencher.iter(move || a.double()) - }); -} - -fn bench_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = -ExtendedPoint::identity(); - c.bench_function("Jubjub point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = -ExtendedPoint::identity(); - c.bench_function("Jubjub point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -// Niels - -fn bench_cached_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = ExtendedPoint::identity().to_niels(); - c.bench_function("Jubjub cached point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = ExtendedPoint::identity().to_niels(); - c.bench_function("Jubjub cached point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_affine_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = AffinePoint::identity().to_niels(); - c.bench_function("Jubjub cached affine point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_affine_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = AffinePoint::identity().to_niels(); - c.bench_function("Jubjub cached affine point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -criterion_group!( - benches, - bench_point_doubling, - bench_point_addition, - bench_point_subtraction, - bench_cached_point_addition, - bench_cached_point_subtraction, - bench_cached_affine_point_addition, - bench_cached_affine_point_subtraction, -); -criterion_main!(benches); diff --git a/jubjub/doc/derive/.gitignore b/jubjub/doc/derive/.gitignore deleted file mode 100644 index 7c974cf29c..0000000000 --- a/jubjub/doc/derive/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.sage.py diff --git a/jubjub/doc/derive/derive.sage b/jubjub/doc/derive/derive.sage deleted file mode 100644 index c0c5310bf4..0000000000 --- a/jubjub/doc/derive/derive.sage +++ /dev/null @@ -1,32 +0,0 @@ -q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -Fq = GF(q) - -# We wish to find a Montgomery curve with B = 1 and A the smallest such -# that (A - 2) / 4 is a small integer. -def get_A(n): - return (n * 4) + 2 - -# A = 2 is invalid (singular curve), so we start at i = 1 (A = 6) -i = 1 - -while True: - A = Fq(get_A(i)) - i = i + 1 - - # We also want that A^2 - 4 is nonsquare. - if ((A^2) - 4).is_square(): - continue - - ec = EllipticCurve(Fq, [0, A, 0, 1, 0]) - o = ec.order() - - if (o % 8 == 0): - o = o // 8 - if is_prime(o): - twist = ec.quadratic_twist() - otwist = twist.order() - if (otwist % 4 == 0): - otwist = otwist // 4 - if is_prime(otwist): - print "A = %s" % A - exit(0) diff --git a/jubjub/doc/evidence/.gitignore b/jubjub/doc/evidence/.gitignore deleted file mode 100644 index 9a0d287f2c..0000000000 --- a/jubjub/doc/evidence/.gitignore +++ /dev/null @@ -1,102 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv -.venv -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - diff --git a/jubjub/doc/evidence/LICENSE b/jubjub/doc/evidence/LICENSE deleted file mode 100644 index 9e181634a2..0000000000 --- a/jubjub/doc/evidence/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017 The Zcash developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/jubjub/doc/evidence/README.md b/jubjub/doc/evidence/README.md deleted file mode 100644 index 26b2e36f23..0000000000 --- a/jubjub/doc/evidence/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Jubjub supporting evidence --------------------------- - -This repository contains supporting evidence that the twisted Edwards curve --x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over -GF(52435875175126190479447740508185965837690552500527637822603658699938581184513), -[also called "Jubjub"](https://z.cash/technology/jubjub.html), -satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html). - -The script ``verify.sage`` is based on -[this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html), -modified - -* to support twisted Edwards curves; -* to generate a file 'primes' containing the primes needed for primality proofs, - if it is not already present; -* to change the directory in which Pocklington proof files are generated - (``proof/`` rather than ``../../../proof``), and to create that directory - if it does not exist. - -Prerequisites: - -* apt-get install sagemath -* pip install sortedcontainers - -Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results. - -Note that the "rigidity" criterion cannot be checked automatically. diff --git a/jubjub/doc/evidence/a b/jubjub/doc/evidence/a deleted file mode 100644 index 3a2e3f4984..0000000000 --- a/jubjub/doc/evidence/a +++ /dev/null @@ -1 +0,0 @@ --1 diff --git a/jubjub/doc/evidence/d b/jubjub/doc/evidence/d deleted file mode 100644 index 767309a726..0000000000 --- a/jubjub/doc/evidence/d +++ /dev/null @@ -1 +0,0 @@ -19257038036680949359750312669786877991949435402254120286184196891950884077233 diff --git a/jubjub/doc/evidence/l b/jubjub/doc/evidence/l deleted file mode 100644 index 83f92d5797..0000000000 --- a/jubjub/doc/evidence/l +++ /dev/null @@ -1 +0,0 @@ -6554484396890773809930967563523245729705921265872317281365359162392183254199 diff --git a/jubjub/doc/evidence/p b/jubjub/doc/evidence/p deleted file mode 100644 index 1dc0557a71..0000000000 --- a/jubjub/doc/evidence/p +++ /dev/null @@ -1 +0,0 @@ -52435875175126190479447740508185965837690552500527637822603658699938581184513 diff --git a/jubjub/doc/evidence/rigid b/jubjub/doc/evidence/rigid deleted file mode 100644 index e560e4087a..0000000000 --- a/jubjub/doc/evidence/rigid +++ /dev/null @@ -1 +0,0 @@ -fully rigid diff --git a/jubjub/doc/evidence/run.sh b/jubjub/doc/evidence/run.sh deleted file mode 100644 index 817f2faf8e..0000000000 --- a/jubjub/doc/evidence/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -sage verify.sage . -grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' - diff --git a/jubjub/doc/evidence/shape b/jubjub/doc/evidence/shape deleted file mode 100644 index 796f74d092..0000000000 --- a/jubjub/doc/evidence/shape +++ /dev/null @@ -1 +0,0 @@ -tedwards diff --git a/jubjub/doc/evidence/verify.sage b/jubjub/doc/evidence/verify.sage deleted file mode 100644 index 1717c0bc6e..0000000000 --- a/jubjub/doc/evidence/verify.sage +++ /dev/null @@ -1,444 +0,0 @@ -import os -import sys -from errno import ENOENT, EEXIST -from sortedcontainers import SortedSet - - -def readfile(fn): - fd = open(fn,'r') - r = fd.read() - fd.close() - return r - -def writefile(fn,s): - fd = open(fn,'w') - fd.write(s) - fd.close() - -def expand2(n): - s = "" - - while n != 0: - j = 16 - while 2**j < abs(n): j += 1 - if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1 - - if abs(abs(n) - 2**j) > 2**(j - 10): - if n > 0: - if s != "": s += " + " - s += str(n) - else: - s += " - " + str(-n) - n = 0 - elif n > 0: - if s != "": s += " + " - s += "2^" + str(j) - n -= 2**j - else: - s += " - 2^" + str(j) - n += 2**j - - return s - -def requirement(fn,istrue): - writefile(fn,str(istrue) + '\n') - return istrue - -def verify(): - try: - os.mkdir('proof') - except OSError as e: - if e.errno != EEXIST: raise - - try: - s = set(map(Integer, readfile('primes').split())) - except IOError, e: - if e.errno != ENOENT: raise - s = set() - - needtofactor = SortedSet() - V = SortedSet() # distinct verified primes - verify_primes(V, s, needtofactor) - verify_pass(V, needtofactor) - - old = V - needtofactor.update(V) - while len(needtofactor) > len(old): - k = len(needtofactor) - len(old) - sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's')) - sys.stdout.flush() - for x in needtofactor: - if x not in old: - for (y, z) in factor(x): - s.add(y) - sys.stdout.write('.') - sys.stdout.flush() - - print('') - - old = needtofactor.copy() - verify_primes(V, s, needtofactor) - - writefile('primes', '\n'.join(map(str, s)) + '\n') - writefile('verify-primes', '\n' + - ''.join(('2\n' if v == 2 else - '%s\n' % (v,v)) for v in V) + - '\n') - - verify_pass(V, needtofactor) - - -def verify_primes(V, s, needtofactor): - for n in sorted(s): - if not n.is_prime() or n in V: continue - needtofactor.add(n-1) - if n == 2: - V.add(n) - continue - for trybase in primes(2,10000): - base = Integers(n)(trybase) - if base^(n-1) != 1: continue - proof = 'Primality proof for n = %s:\n' % n - proof += '

Take b = %s.\n' % base - proof += '

b^(n-1) mod n = 1.\n' - f = factor(1) - for v in reversed(V): - if f.prod()^2 <= n: - if n % v == 1: - u = base^((n-1)/v)-1 - if u.is_unit(): - if v == 2: - proof += '

2 is prime.\n' - else: - proof += '

%s is prime.\n' % (v,v) - proof += '
b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u) - f *= factor(v)^(n-1).valuation(v) - if f.prod()^2 <= n: continue - if n % f.prod() != 1: continue - proof += '

(%s) divides n-1.\n' % f - proof += '

(%s)^2 > n.\n' % f - proof += "

n is prime by Pocklington's theorem.\n" - proof += '\n' - writefile('proof/%s.html' % n,proof) - V.add(n) - break - - -def verify_pass(V, needtofactor): - p = Integer(readfile('p')) - k = GF(p) - kz. = k[] - l = Integer(readfile('l')) - x0 = Integer(readfile('x0')) - y0 = Integer(readfile('y0')) - x1 = Integer(readfile('x1')) - y1 = Integer(readfile('y1')) - shape = readfile('shape').strip() - rigid = readfile('rigid').strip() - - safefield = True - safeeq = True - safebase = True - saferho = True - safetransfer = True - safedisc = True - saferigid = True - safeladder = True - safetwist = True - safecomplete = True - safeind = True - - pstatus = 'Unverified' - if not p.is_prime(): pstatus = 'False' - needtofactor.add(p) - if p in V: pstatus = 'True' - if pstatus != 'True': safefield = False - writefile('verify-pisprime',pstatus + '\n') - - pstatus = 'Unverified' - if not l.is_prime(): pstatus = 'False' - needtofactor.add(l) - if l in V: pstatus = 'True' - if pstatus != 'True': safebase = False - writefile('verify-lisprime',pstatus + '\n') - - writefile('expand2-p','= %s\n' % expand2(p)) - writefile('expand2-l','
= %s\n' % expand2(l)) - - writefile('hex-p',hex(p) + '\n') - writefile('hex-l',hex(l) + '\n') - writefile('hex-x0',hex(x0) + '\n') - writefile('hex-x1',hex(x1) + '\n') - writefile('hex-y0',hex(y0) + '\n') - writefile('hex-y1',hex(y1) + '\n') - - gcdlpis1 = gcd(l,p) == 1 - safetransfer &= requirement('verify-gcdlp1',gcdlpis1) - - writefile('verify-movsafe','Unverified\n') - writefile('verify-embeddingdegree','Unverified\n') - if gcdlpis1 and l.is_prime(): - u = Integers(l)(p) - d = l-1 - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = l-1 - for v in V: - while d % v == 0: - if u^(d/v) != 1: break - d /= v - safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100) - writefile('verify-embeddingdegree','%s
= (l-1)/%s\n' % (d,(l-1)/d)) - - t = p+1-l*round((p+1)/l) - if l^2 > 16*p: - writefile('verify-trace','%s\n' % t) - f = factor(1) - d = (p+1-t)/l - needtofactor.add(d) - for v in V: - while d % v == 0: - d //= v - f *= factor(v) - writefile('verify-cofactor','%s\n' % f) - else: - writefile('verify-trace','Unverified\n') - writefile('verify-cofactor','Unverified\n') - - D = t^2-4*p - needtofactor.add(D) - for v in V: - while D % v^2 == 0: D /= v^2 - if prod([v for v in V if D % v == 0]) != -D: - writefile('verify-disc','Unverified\n') - writefile('verify-discisbig','Unverified\n') - safedisc = False - else: - f = -prod([factor(v) for v in V if D % v == 0]) - if D % 4 != 1: - D *= 4 - f = factor(4) * f - Dbits = (log(-D)/log(2)).numerical_approx() - writefile('verify-disc','%s
= %s
≈ -2^%.1f\n' % (D,f,Dbits)) - safedisc &= requirement('verify-discisbig',D < -2^100) - - pi4 = 0.78539816339744830961566084581987572105 - rho = log(pi4*l)/log(4) - writefile('verify-rho','%.1f\n' % rho) - saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100) - - twistl = 'Unverified' - d = p+1+t - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = p+1+t - for v in V: - if d % v == 0: - if twistl == 'Unverified' or v > twistl: twistl = v - - writefile('verify-twistl','%s\n' % twistl) - writefile('verify-twistembeddingdegree','Unverified\n') - writefile('verify-twistmovsafe','Unverified\n') - if twistl == 'Unverified': - writefile('hex-twistl','Unverified\n') - writefile('expand2-twistl','Unverified\n') - writefile('verify-twistcofactor','Unverified\n') - writefile('verify-gcdtwistlp1','Unverified\n') - writefile('verify-twistrho','Unverified\n') - safetwist = False - else: - writefile('hex-twistl',hex(twistl) + '\n') - writefile('expand2-twistl','
= %s\n' % expand2(twistl)) - f = factor(1) - d = (p+1+t)/twistl - needtofactor.add(d) - for v in V: - while d % v == 0: - d //= v - f *= factor(v) - writefile('verify-twistcofactor','%s\n' % f) - gcdtwistlpis1 = gcd(twistl,p) == 1 - safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1) - - movsafe = 'Unverified' - embeddingdegree = 'Unverified' - if gcdtwistlpis1 and twistl.is_prime(): - u = Integers(twistl)(p) - d = twistl-1 - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = twistl-1 - for v in V: - while d % v == 0: - if u^(d/v) != 1: break - d /= v - safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100) - writefile('verify-twistembeddingdegree',"%s
= (l'-1)/%s\n" % (d,(twistl-1)/d)) - - rho = log(pi4*twistl)/log(4) - writefile('verify-twistrho','%.1f\n' % rho) - safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100) - - precomp = 0 - joint = l - needtofactor.add(p+1-t) - needtofactor.add(p+1+t) - for v in V: - d1 = p+1-t - d2 = p+1+t - while d1 % v == 0 or d2 % v == 0: - if d1 % v == 0: d1 //= v - if d2 % v == 0: d2 //= v - # best case for attack: cyclic; each power is usable - # also assume that kangaroo is as efficient as rho - if v + sqrt(pi4*joint/v) < sqrt(pi4*joint): - precomp += v - joint /= v - - rho = log(precomp + sqrt(pi4 * joint))/log(2) - writefile('verify-jointrho','%.1f\n' % rho) - safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100) - - - x0 = k(x0) - y0 = k(y0) - x1 = k(x1) - y1 = k(y1) - - if shape in ('edwards', 'tedwards'): - d = Integer(readfile('d')) - a = 1 - if shape == 'tedwards': - a = Integer(readfile('a')) - - writefile('verify-shape','Twisted Edwards\n') - writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d)) - if a == 1: - writefile('verify-shape','Edwards\n') - writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d) - - a = k(a) - d = k(d) - elliptic = a*d*(a-d) - level0 = a*x0^2+y0^2-1-d*x0^2*y0^2 - level1 = a*x1^2+y1^2-1-d*x1^2*y1^2 - - if shape == 'montgomery': - writefile('verify-shape','Montgomery\n') - A = Integer(readfile('A')) - B = Integer(readfile('B')) - equation = '%sy^2 = x^3%+dx^2+x' % (B,A) - if B == 1: - equation = 'y^2 = x^3%+dx^2+x' % A - writefile('verify-equation',equation + '\n') - - A = k(A) - B = k(B) - elliptic = B*(A^2-4) - level0 = B*y0^2-x0^3-A*x0^2-x0 - level1 = B*y1^2-x1^3-A*x1^2-x1 - - if shape == 'shortw': - writefile('verify-shape','short Weierstrass\n') - a = Integer(readfile('a')) - b = Integer(readfile('b')) - writefile('verify-equation','y^2 = x^3%+dx%+d\n' % (a,b)) - - a = k(a) - b = k(b) - elliptic = 4*a^3+27*b^2 - level0 = y0^2-x0^3-a*x0-b - level1 = y1^2-x1^3-a*x1-b - - writefile('verify-elliptic',str(elliptic) + '\n') - safeeq &= requirement('verify-iselliptic',elliptic != 0) - safebase &= requirement('verify-isoncurve0',level0 == 0) - safebase &= requirement('verify-isoncurve1',level1 == 0) - - if shape in ('edwards', 'tedwards'): - A = 2*(a+d)/(a-d) - B = 4/(a-d) - x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0 - x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1 - shape = 'montgomery' - - if shape == 'montgomery': - a = (3-A^2)/(3*B^2) - b = (2*A^3-9*A)/(27*B^3) - x0,y0 = (x0+A/3)/B,y0/B - x1,y1 = (x1+A/3)/B,y1/B - shape = 'shortw' - - try: - E = EllipticCurve([a,b]) - numorder2 = 0 - numorder4 = 0 - for P in E(0).division_points(4): - if P != 0 and 2*P == 0: - numorder2 += 1 - if 2*P != 0 and 4*P == 0: - numorder4 += 1 - writefile('verify-numorder2',str(numorder2) + '\n') - writefile('verify-numorder4',str(numorder4) + '\n') - completesingle = False - completemulti = False - if numorder4 == 2 and numorder2 == 1: - # complete edwards form, and montgomery with unique point of order 2 - completesingle = True - completemulti = True - # should extend this to allow complete twisted hessian - safecomplete &= requirement('verify-completesingle',completesingle) - safecomplete &= requirement('verify-completemulti',completemulti) - safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0) - writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n') - writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n') - except: - writefile('verify-numorder2','Unverified\n') - writefile('verify-numorder4','Unverified\n') - writefile('verify-ltimesbase1','Unverified\n') - writefile('verify-cofactorbase01','Unverified\n') - safecomplete = False - - montladder = False - for r,e in (z^3+a*z+b).roots(): - if (3*r^2+a).is_square(): - montladder = True - safeladder &= requirement('verify-montladder',montladder) - - indistinguishability = False - elligator2 = False - if (p+1-t) % 2 == 0: - if b != 0: - indistinguishability = True - elligator2 = True - safeind &= requirement('verify-indistinguishability',indistinguishability) - writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2]) - - saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid') - - safecurve = True - safecurve &= requirement('verify-safefield',safefield) - safecurve &= requirement('verify-safeeq',safeeq) - safecurve &= requirement('verify-safebase',safebase) - safecurve &= requirement('verify-saferho',saferho) - safecurve &= requirement('verify-safetransfer',safetransfer) - safecurve &= requirement('verify-safedisc',safedisc) - safecurve &= requirement('verify-saferigid',saferigid) - safecurve &= requirement('verify-safeladder',safeladder) - safecurve &= requirement('verify-safetwist',safetwist) - safecurve &= requirement('verify-safecomplete',safecomplete) - safecurve &= requirement('verify-safeind',safeind) - requirement('verify-safecurve',safecurve) - -originaldir = os.open('.',os.O_RDONLY) -for i in range(1,len(sys.argv)): - os.fchdir(originaldir) - os.chdir(sys.argv[i]) - verify() - diff --git a/jubjub/doc/evidence/x0 b/jubjub/doc/evidence/x0 deleted file mode 100644 index 3b2097a36c..0000000000 --- a/jubjub/doc/evidence/x0 +++ /dev/null @@ -1 +0,0 @@ -11076627216317271660298050606127911965867021807910416450833192264015104452986 diff --git a/jubjub/doc/evidence/x1 b/jubjub/doc/evidence/x1 deleted file mode 100644 index c8c8fc3088..0000000000 --- a/jubjub/doc/evidence/x1 +++ /dev/null @@ -1 +0,0 @@ -8076246640662884909881801758704306714034609987455869804520522091855516602923 diff --git a/jubjub/doc/evidence/y0 b/jubjub/doc/evidence/y0 deleted file mode 100644 index b47cd2764f..0000000000 --- a/jubjub/doc/evidence/y0 +++ /dev/null @@ -1 +0,0 @@ -44412834903739585386157632289020980010620626017712148233229312325549216099227 diff --git a/jubjub/doc/evidence/y1 b/jubjub/doc/evidence/y1 deleted file mode 100644 index a46479fb58..0000000000 --- a/jubjub/doc/evidence/y1 +++ /dev/null @@ -1 +0,0 @@ -13262374693698910701929044844600465831413122818447359594527400194675274060458 diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs deleted file mode 100644 index 1398558477..0000000000 --- a/jubjub/src/fr.rs +++ /dev/null @@ -1,1132 +0,0 @@ -//! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ -//! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` - -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use ff::{Field, PrimeField}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. Elements of Fr are always in -// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Fr(pub(crate) [u64; 4]); - -impl fmt::Debug for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl fmt::Display for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl From for Fr { - fn from(val: u64) -> Fr { - Fr([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Fr { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl PartialEq for Fr { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Fr { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fr([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - ]) - } -} - -/// Constant representing the modulus -/// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 -pub const MODULUS: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb7, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -const MODULUS_BYTES: [u8; 32] = [ - 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, - 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, -]; - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 252; - -// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: Fr = Fr([ - 0x720b_1b19_d49e_a8f1, - 0xbf4a_a361_01f1_3a58, - 0x5fa8_cc96_8193_ccbb, - 0x0e70_cbdc_7dcc_f3ac, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 1; - -// 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: Fr = Fr([ - 0xaa9f_02ab_1d61_24de, - 0xb352_4a64_6611_2932, - 0x7342_2612_15ac_260b, - 0x04d6_b87b_1da2_59e2, -]); - -impl<'a> Neg for &'a Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - self.neg() - } -} - -impl Neg for Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn sub(self, rhs: &'b Fr) -> Fr { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn add(self, rhs: &'b Fr) -> Fr { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn mul(self, rhs: &'b Fr) -> Fr { - // Schoolbook multiplication - - self.mul(rhs) - } -} - -impl_binops_additive!(Fr, Fr); -impl_binops_multiplicative!(Fr, Fr); - -/// INV = -(r^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x1ba3_a358_ef78_8ef9; - -/// R = 2^256 mod r -const R: Fr = Fr([ - 0x25f8_0bb3_b996_07d9, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, -]); - -/// R^2 = 2^512 mod r -const R2: Fr = Fr([ - 0x6771_9aa4_95e5_7731, - 0x51b0_cef0_9ce3_fc26, - 0x69da_b7fa_c026_e9a5, - 0x04f6_547b_8d12_7688, -]); - -/// R^3 = 2^768 mod r -const R3: Fr = Fr([ - 0xe0d6_c656_3d83_0544, - 0x323e_3883_598d_0f85, - 0xf0fe_a300_4c2e_2ba8, - 0x0587_4f84_9467_37ec, -]); - -impl Default for Fr { - fn default() -> Self { - Self::zero() - } -} - -impl Fr { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Fr { - Fr([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Fr { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Fr { - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a field element into an element of `Fr`, failing if the input - /// is not canonical (is not smaller than r). - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Fr([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Fr` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fr::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// an element of Fr by reducing modulo r. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { - Fr::from_u512([ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - u64::from_le_bytes(bytes[32..40].try_into().unwrap()), - u64::from_le_bytes(bytes[40..48].try_into().unwrap()), - u64::from_le_bytes(bytes[48..56].try_into().unwrap()), - u64::from_le_bytes(bytes[56..64].try_into().unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Fr { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multiplied by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - let d0 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Fr([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Fr` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Fr(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Fr { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = self.pow_vartime(&[ - 0xb425_c397_b5bd_cb2e, - 0x299a_0824_f332_0420, - 0x4199_cec0_404d_0ec0, - 0x039f_6d3a_994c_ebea, - ]); - - CtOption::new( - sqrt, - (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp.mul_assign(self); - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - /// - /// **This operation is variable time with respect - /// to the exponent.** If the exponent is fixed, - /// this operation is effectively constant time. - pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Fr, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t1 = self.square(); - let mut t0 = t1.square(); - let mut t3 = t0 * t1; - let t6 = t3 * self; - let t7 = t6 * t1; - let t12 = t7 * t3; - let t13 = t12 * t0; - let t16 = t12 * t3; - let t2 = t13 * t3; - let t15 = t16 * t3; - let t19 = t2 * t0; - let t9 = t15 * t3; - let t18 = t9 * t3; - let t14 = t18 * t1; - let t4 = t18 * t0; - let t8 = t18 * t3; - let t17 = t14 * t3; - let t11 = t8 * t3; - t1 = t17 * t3; - let t5 = t11 * t3; - t3 = t5 * t0; - t0 = t5.square(); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t19); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t18); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t17); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t16); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 8); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 14); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 2); - t0.mul_assign(self); - square_assign_multi(&mut t0, 6); - t0.mul_assign(self); - square_assign_multi(&mut t0, 9); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t12); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 4); - t0.mul_assign(&t6); - square_assign_multi(&mut t0, 10); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t4); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t1); - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline] - #[allow(clippy::too_many_arguments)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Fr([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies this element by another element - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts another element from this element. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Fr([d0, d1, d2, d3]) - } - - /// Adds this element to another element. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fr([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates this element. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl From for [u8; 32] { - fn from(value: Fr) -> [u8; 32] { - value.to_bytes() - } -} - -impl<'a> From<&'a Fr> for [u8; 32] { - fn from(value: &'a Fr) -> [u8; 32] { - value.to_bytes() - } -} - -impl Field for Fr { - fn random(rng: &mut R) -> Self { - let mut buf = [0; 64]; - rng.fill_bytes(&mut buf); - Self::from_bytes_wide(&buf) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn is_zero(&self) -> bool { - self.ct_eq(&Self::zero()).into() - } - - #[must_use] - fn square(&self) -> Self { - self.square() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } - - fn invert(&self) -> CtOption { - self.invert() - } - - fn sqrt(&self) -> CtOption { - self.sqrt() - } -} - -impl PrimeField for Fr { - type Repr = [u8; 32]; - type ReprEndianness = byteorder::LittleEndian; - - fn from_repr(r: Self::Repr) -> Option { - let res = Self::from_bytes(&r); - if res.is_some().into() { - Some(res.unwrap()) - } else { - None - } - } - - fn to_repr(&self) -> Self::Repr { - self.to_bytes() - } - - fn is_odd(&self) -> bool { - self.to_bytes()[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS_BYTES - } - - const NUM_BITS: u32 = MODULUS_BITS; - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -#[test] -fn test_inv() { - // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Fr::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Fr::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Fr::zero(), Fr::zero()); - assert_eq!(Fr::one(), Fr::one()); - assert_eq!(R2, R2); - - assert!(Fr::zero() != Fr::one()); - assert!(Fr::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Fr::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Fr::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ] - ); - - assert_eq!( - (-&Fr::one()).to_bytes(), - [ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Fr::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::zero() - ); - - assert_eq!( - Fr::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::one() - ); - - assert_eq!( - Fr::from_bytes(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!(bool::from( - Fr::from_bytes(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_some() - )); - - // modulus is invalid - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - // Anything larger than the modulus is invalid - assert!(bool::from( - Fr::from_bytes(&[ - 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 - ]) - .is_none() - )); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Fr::zero(), - Fr::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Fr::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Fr::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Fr::from_bytes_wide(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Fr::one(), - Fr::from_bytes_wide(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Fr([ - 0x8b75_c901_5ae4_2a22, - 0xe590_82e7_bf9e_38b8, - 0x6440_c912_61da_51b3, - 0x0a5e_07ff_b209_91cf, - ]), - Fr::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Fr::zero(), -&Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); -} - -#[cfg(test)] -const LARGEST: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb6, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Fr([ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9 - ]) - ); - - let mut tmp = LARGEST; - tmp += &Fr([1, 0, 0, 0]); - - assert_eq!(tmp, Fr::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Fr([1, 0, 0, 0])); - - let tmp = -&Fr::zero(); - assert_eq!(tmp, Fr::zero()); - let tmp = -&Fr([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Fr::zero()); - - let mut tmp = Fr::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert!(bool::from(Fr::zero().invert().is_none())); - assert_eq!(Fr::one().invert().unwrap(), Fr::one()); - assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Fr::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let r_minus_2 = [ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&r_minus_2); - r3 = r3.pow(&r_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - let mut square = Fr([ - // r - 2 - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if bool::from(square_root.is_none()) { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Fr::one(); - } - - assert_eq!(47, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Fr::from_raw([ - 0x25f8_0bb3_b996_07d8, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, - ]), - Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); - - assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); -} diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs deleted file mode 100644 index d55dd6c9b1..0000000000 --- a/jubjub/src/lib.rs +++ /dev/null @@ -1,1756 +0,0 @@ -//! This crate provides an implementation of the **Jubjub** elliptic curve and its associated -//! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. -//! -//! # API -//! -//! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic -//! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points -//! * `Fq`, which is the base field of Jubjub -//! * `Fr`, which is the scalar field of Jubjub -//! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. -//! -//! # Constant Time -//! -//! All operations are constant time unless explicitly noted; these functions will contain -//! "vartime" in their name and they will be documented as variable time. -//! -//! This crate uses the `subtle` crate to perform constant-time operations. - -#![no_std] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] -#![deny(unsafe_code)] -// This lint is described at -// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl -// In our library, some of the arithmetic will necessarily involve various binary -// operators, and so this lint is triggered unnecessarily. -#![allow(clippy::suspicious_arithmetic_impl)] - -#[cfg(test)] -#[macro_use] -extern crate std; - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use ff::Field; -use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, - prime::PrimeGroup, - Curve, Group, GroupEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -#[macro_use] -mod util; - -mod fr; -pub use bls12_381::Scalar as Fq; -pub use fr::Fr; - -/// A better name than Fr. -pub type Scalar = Fr; - -const FR_MODULUS_BYTES: [u8; 32] = [ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, - 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, -]; - -/// This represents a Jubjub point in the affine `(u, v)` -/// coordinates. -#[derive(Clone, Copy, Debug, Eq)] -pub struct AffinePoint { - u: Fq, - v: Fq, -} - -impl fmt::Display for AffinePoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl Neg for AffinePoint { - type Output = AffinePoint; - - /// This computes the negation of a point `P = (u, v)` - /// as `-P = (-u, v)`. - #[inline] - fn neg(self) -> AffinePoint { - AffinePoint { - u: -self.u, - v: self.v, - } - } -} - -impl ConstantTimeEq for AffinePoint { - fn ct_eq(&self, other: &Self) -> Choice { - self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) - } -} - -impl PartialEq for AffinePoint { - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for AffinePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffinePoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - } - } -} - -/// This represents an extended point `(U, V, Z, T1, T2)` -/// with `Z` nonzero, corresponding to the affine point -/// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. -/// -/// You can do the following things with a point in this -/// form: -/// -/// * Convert it into a point in the affine form. -/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. -/// * Double it using `double()`. -/// * Compare it with another extended point using `PartialEq` or `ct_eq()`. -#[derive(Clone, Copy, Debug, Eq)] -pub struct ExtendedPoint { - u: Fq, - v: Fq, - z: Fq, - t1: Fq, - t2: Fq, -} - -impl fmt::Display for ExtendedPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl ConstantTimeEq for ExtendedPoint { - fn ct_eq(&self, other: &Self) -> Choice { - // (u/z, v/z) = (u'/z', v'/z') is implied by - // (uz'z = u'z'z) and - // (vz'z = v'z'z) - // as z and z' are always nonzero. - - (self.u * other.z).ct_eq(&(other.u * self.z)) - & (self.v * other.z).ct_eq(&(other.v * self.z)) - } -} - -impl ConditionallySelectable for ExtendedPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedPoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t1: Fq::conditional_select(&a.t1, &b.t1, choice), - t2: Fq::conditional_select(&a.t2, &b.t2, choice), - } - } -} - -impl PartialEq for ExtendedPoint { - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl Sum for ExtendedPoint -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Neg for ExtendedPoint { - type Output = ExtendedPoint; - - /// Computes the negation of a point `P = (U, V, Z, T)` - /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` - /// is made without loss of generality. - #[inline] - fn neg(self) -> ExtendedPoint { - ExtendedPoint { - u: -self.u, - v: self.v, - z: self.z, - t1: -self.t1, - t2: self.t2, - } - } -} - -impl From for ExtendedPoint { - /// Constructs an extended point (with `Z = 1`) from - /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. - fn from(affine: AffinePoint) -> ExtendedPoint { - ExtendedPoint { - u: affine.u, - v: affine.v, - z: Fq::one(), - t1: affine.u, - t2: affine.v, - } - } -} - -impl<'a> From<&'a ExtendedPoint> for AffinePoint { - /// Constructs an affine point from an extended point - /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` - /// as Z is always nonzero. **This requires a field inversion - /// and so it is recommended to perform these in a batch - /// using [`batch_normalize`](crate::batch_normalize) instead.** - fn from(extended: &'a ExtendedPoint) -> AffinePoint { - // Z coordinate is always nonzero, so this is - // its inverse. - let zinv = extended.z.invert().unwrap(); - - AffinePoint { - u: extended.u * zinv, - v: extended.v * zinv, - } - } -} - -impl From for AffinePoint { - fn from(extended: ExtendedPoint) -> AffinePoint { - AffinePoint::from(&extended) - } -} - -/// This is a pre-processed version of an affine point `(u, v)` -/// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an -/// [`ExtendedPoint`](crate::ExtendedPoint). -#[derive(Clone, Copy, Debug)] -pub struct AffineNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - t2d: Fq, -} - -impl AffineNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffineNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = AffineNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += AffineNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); - -impl ConditionallySelectable for AffineNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffineNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -/// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` -/// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. -#[derive(Clone, Copy, Debug)] -pub struct ExtendedNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - z: Fq, - t2d: Fq, -} - -impl ConditionallySelectable for ExtendedNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -impl ExtendedNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - z: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = ExtendedNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); - -// `d = -(10240/10241)` -const EDWARDS_D: Fq = Fq::from_raw([ - 0x0106_5fd6_d634_3eb1, - 0x292d_7f6d_3757_9d26, - 0xf5fd_9207_e6bd_7fd4, - 0x2a93_18e7_4bfa_2b48, -]); - -// `2*d` -const EDWARDS_D2: Fq = Fq::from_raw([ - 0x020c_bfad_ac68_7d62, - 0x525a_feda_6eaf_3a4c, - 0xebfb_240f_cd7a_ffa8, - 0x5526_31ce_97f4_5691, -]); - -impl AffinePoint { - /// Constructs the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffinePoint { - u: Fq::zero(), - v: Fq::one(), - } - } - - /// Determines if this point is the identity. - pub fn is_identity(&self) -> Choice { - ExtendedPoint::from(*self).is_identity() - } - - /// Multiplies this point by the cofactor, producing an - /// `ExtendedPoint` - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - ExtendedPoint::from(*self).mul_by_cofactor() - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - ExtendedPoint::from(*self).is_small_order() - } - - /// Determines if this point is torsion free and so is - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - ExtendedPoint::from(*self).is_torsion_free() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - let extended = ExtendedPoint::from(*self); - extended.is_torsion_free() & (!extended.is_identity()) - } - - /// Converts this element into its byte representation. - pub fn to_bytes(&self) -> [u8; 32] { - let mut tmp = self.v.to_bytes(); - let u = self.u.to_bytes(); - - // Encode the sign of the u-coordinate in the most - // significant bit. - tmp[31] |= u[0] << 7; - - tmp - } - - /// Attempts to interpret a byte representation of an - /// affine point, failing if the element is not on - /// the curve or non-canonical. - pub fn from_bytes(mut b: [u8; 32]) -> CtOption { - // Grab the sign bit from the representation - let sign = b[31] >> 7; - - // Mask away the sign bit - b[31] &= 0b0111_1111; - - // Interpret what remains as the v-coordinate - Fq::from_bytes(&b).and_then(|v| { - // -u^2 + v^2 = 1 + d.u^2.v^2 - // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) - // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) - // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) - // u^2 (1 + d.v^2) = v^2 - 1 (factor) - // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) - // We know that (1 + d.v^2) is nonzero for all v: - // (1 + d.v^2) = 0 - // d.v^2 = -1 - // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square - - let v2 = v.square(); - - ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) - .sqrt() - .and_then(|u| { - // Fix the sign of `u` if necessary - let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); - let u_negated = -u; - let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); - - CtOption::new(AffinePoint { u: final_u, v }, Choice::from(1u8)) - }) - }) - } - - /// Returns the `u`-coordinate of this point. - pub fn get_u(&self) -> Fq { - self.u - } - - /// Returns the `v`-coordinate of this point. - pub fn get_v(&self) -> Fq { - self.v - } - - /// Returns an `ExtendedPoint` for use in arithmetic operations. - pub const fn to_extended(&self) -> ExtendedPoint { - ExtendedPoint { - u: self.u, - v: self.v, - z: Fq::one(), - t1: self.u, - t2: self.v, - } - } - - /// Performs a pre-processing step that produces an `AffineNielsPoint` - /// for use in multiple additions. - pub const fn to_niels(&self) -> AffineNielsPoint { - AffineNielsPoint { - v_plus_u: Fq::add(&self.v, &self.u), - v_minus_u: Fq::sub(&self.v, &self.u), - t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), - } - } - - /// Constructs an AffinePoint given `u` and `v` without checking - /// that the point is on the curve. - pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { - AffinePoint { u, v } - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let u2 = self.u.square(); - let v2 = self.v.square(); - - v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 - } -} - -impl ExtendedPoint { - /// Constructs an extended point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedPoint { - u: Fq::zero(), - v: Fq::one(), - z: Fq::one(), - t1: Fq::zero(), - t2: Fq::zero(), - } - } - - /// Determines if this point is the identity. - pub fn is_identity(&self) -> Choice { - // If this point is the identity, then - // u = 0 * z = 0 - // and v = 1 * z = z - self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - // We only need to perform two doublings, since the 2-torsion - // points are (0, 1) and (0, -1), and so we only need to check - // that the u-coordinate of the result is zero to see if the - // point is small order. - self.double().double().u.ct_eq(&Fq::zero()) - } - - /// Determines if this point is torsion free and so is contained - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - self.multiply(&FR_MODULUS_BYTES).is_identity() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - self.is_torsion_free() & (!self.is_identity()) - } - - /// Multiplies this element by the cofactor `8`. - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - self.double().double().double() - } - - /// Performs a pre-processing step that produces an `ExtendedNielsPoint` - /// for use in multiple additions. - pub fn to_niels(&self) -> ExtendedNielsPoint { - ExtendedNielsPoint { - v_plus_u: self.v + self.u, - v_minus_u: self.v - self.u, - z: self.z, - t2d: self.t1 * self.t2 * EDWARDS_D2, - } - } - - /// Computes the doubling of a point more efficiently than a point can - /// be added to itself. - pub fn double(&self) -> ExtendedPoint { - // Doubling is more efficient (three multiplications, four squarings) - // when we work within the projective coordinate space (U:Z, V:Z). We - // rely on the most efficient formula, "dbl-2008-bbjlp", as described - // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. - // - // See - // for more information. - // - // We differ from the literature in that we use (u, v) rather than - // (x, y) coordinates. We also have the constant `a = -1` implied. Let - // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) - // as follows: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // H = 2 * z^2 - // J = F - H - // U = (B - C - D) * J - // V = F * (- C - D) - // Z = F * J - // - // If we compute K = D + C, we can rewrite this: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = F - H - // U = (B - K) * J - // V = F * (-K) - // Z = F * J - // - // In order to avoid the unnecessary negation of K, - // we will negate J, transforming the result into - // an equivalent point with a negated z-coordinate. - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = H - F - // U = (B - K) * J - // V = F * K - // Z = F * J - // - // Let us rename some variables to simplify: - // - // UV2 = (u + v)^2 - // UU = u^2 - // VV = v^2 - // VVmUU = VV - UU - // VVpUU = VV + UU - // ZZ2 = 2 * z^2 - // J = ZZ2 - VVmUU - // U = (UV2 - VVpUU) * J - // V = VVmUU * VVpUU - // Z = VVmUU * J - // - // We wish to obtain two factors of T = UV/Z. - // - // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) - // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU - // = (UV2 - VVpUU) * VVpUU - // - // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. - - let uu = self.u.square(); - let vv = self.v.square(); - let zz2 = self.z.square().double(); - let uv2 = (self.u + self.v).square(); - let vv_plus_uu = vv + uu; - let vv_minus_uu = vv - uu; - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: uv2 - vv_plus_uu, - v: vv_plus_uu, - z: vv_minus_uu, - t: zz2 - vv_minus_uu, - } - .into_extended() - } - - #[inline] - fn multiply(self, by: &[u8; 32]) -> Self { - self.to_niels().multiply(by) - } - - /// Converts a batch of projective elements into affine elements. - /// - /// This function will panic if `p.len() != q.len()`. - /// - /// This costs 5 multiplications per element, and a field inversion. - fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fq::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `u` field of `AffinePoint` to store the product - // of previous z-coordinates seen. - q.u = acc; - acc *= &p.z; - } - - // This is the inverse, as all z-coordinates are nonzero. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().zip(q.iter_mut()).rev() { - // Compute tmp = 1/z - let tmp = q.u * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc *= &p.z; - - // Set the coordinates to the correct value - q.u = p.u * &tmp; // Multiply by 1/z - q.v = p.v * &tmp; // Multiply by 1/z - } - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let affine = AffinePoint::from(*self); - - self.z != Fq::zero() - && affine.is_on_curve_vartime() - && (affine.u * affine.v * self.z == self.t1 * self.t2) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative!(ExtendedPoint, Fr); - -impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - // We perform addition in the extended coordinates. Here we use - // a formula presented by Hisil, Wong, Carter and Dawson in - // "Twisted Edward Curves Revisited" which only requires 8M. - // - // A = (V1 - U1) * (V2 - U2) - // B = (V1 + U1) * (V2 + U2) - // C = 2d * T1 * T2 - // D = 2 * Z1 * Z2 - // E = B - A - // F = D - C - // G = D + C - // H = B + A - // U3 = E * F - // Y3 = G * H - // Z3 = F * G - // T3 = E * H - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); - -impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - // This is identical to the addition formula for `ExtendedNielsPoint`, - // except we can assume that `other.z` is one, so that we perform - // 7 multiplications. - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, AffineNielsPoint); - -impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedPoint); - -impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b AffinePoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, AffinePoint); - -/// This is a "completed" point produced during a point doubling or -/// addition routine. These points exist in the `(U:Z, V:T)` model -/// of the curve. This is not exposed in the API because it is -/// an implementation detail. -struct CompletedPoint { - u: Fq, - v: Fq, - z: Fq, - t: Fq, -} - -impl CompletedPoint { - /// This converts a completed point into an extended point by - /// homogenizing: - /// - /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) - /// - /// The resulting T coordinate is utvz/zt = uv, and so - /// T1 = u, T2 = v, without loss of generality. - #[inline] - fn into_extended(self) -> ExtendedPoint { - ExtendedPoint { - u: self.u * self.t, - v: self.v * self.z, - z: self.z * self.t, - t1: self.u, - t2: self.v, - } - } -} - -impl Default for AffinePoint { - /// Returns the identity. - fn default() -> AffinePoint { - AffinePoint::identity() - } -} - -impl Default for ExtendedPoint { - /// Returns the identity. - fn default() -> ExtendedPoint { - ExtendedPoint::identity() - } -} - -/// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using -/// only a single inversion for the entire batch. This normalization results in -/// all of the points having a Z-coordinate of one. Further, an iterator is -/// returned which can be used to obtain `AffinePoint`s for each element in the -/// slice. -/// -/// This costs 5 multiplications per element, and a field inversion. -pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator + 'a { - let mut acc = Fq::one(); - for p in v.iter_mut() { - // We use the `t1` field of `ExtendedPoint` to store the product - // of previous z-coordinates seen. - p.t1 = acc; - acc *= &p.z; - } - - // This is the inverse, as all z-coordinates are nonzero. - acc = acc.invert().unwrap(); - - for p in v.iter_mut().rev() { - let mut q = *p; - - // Compute tmp = 1/z - let tmp = q.t1 * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc *= &q.z; - - // Set the coordinates to the correct value - q.u *= &tmp; // Multiply by 1/z - q.v *= &tmp; // Multiply by 1/z - q.z = Fq::one(); // z-coordinate is now one - q.t1 = q.u; - q.t2 = q.v; - - *p = q; - } - - // All extended points are now normalized, but the type - // doesn't encode this fact. Let us offer affine points - // to the caller. - - v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) -} - -impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.to_niels().multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint); - -/// This represents a point in the prime-order subgroup of Jubjub, in extended -/// coordinates. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct SubgroupPoint(ExtendedPoint); - -impl From for ExtendedPoint { - fn from(val: SubgroupPoint) -> ExtendedPoint { - val.0 - } -} - -impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint { - fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint { - &val.0 - } -} - -impl fmt::Display for SubgroupPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl ConditionallySelectable for SubgroupPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl SubgroupPoint { - /// Constructs an AffinePoint given `u` and `v` without checking that the point is on - /// the curve or in the prime-order subgroup. - /// - /// This should only be used for hard-coding constants (e.g. fixed generators); in all - /// other cases, use [`SubgroupPoint::from_bytes`] instead. - /// - /// [`SubgroupPoint::from_bytes`]: SubgroupPoint#impl-GroupEncoding - pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { - SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) - } -} - -impl Sum for SubgroupPoint -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Neg for SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn neg(self) -> SubgroupPoint { - SubgroupPoint(-self.0) - } -} - -impl Neg for &SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn neg(self) -> SubgroupPoint { - SubgroupPoint(-self.0) - } -} - -impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint { - self + &other.0 - } -} - -impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint { - self - &other.0 - } -} - -impl_binops_additive!(ExtendedPoint, SubgroupPoint); - -impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { - SubgroupPoint(self.0 + &other.0) - } -} - -impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { - SubgroupPoint(self.0 - &other.0) - } -} - -impl_binops_additive!(SubgroupPoint, SubgroupPoint); - -impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - fn mul(self, other: &'b Fr) -> SubgroupPoint { - SubgroupPoint(self.0.multiply(&other.to_bytes())) - } -} - -impl_binops_multiplicative!(SubgroupPoint, Fr); - -impl Group for ExtendedPoint { - type Scalar = Fr; - - fn random(rng: &mut R) -> Self { - loop { - let v = Fq::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // See AffinePoint::from_bytes for details. - let v2 = v.square(); - let p = ((v2 - Fq::one()) - * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) - .sqrt() - .map(|u| AffinePoint { - u: if flip_sign { -u } else { u }, - v, - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - AffinePoint::generator().into() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl Group for SubgroupPoint { - type Scalar = Fr; - - fn random(rng: &mut R) -> Self { - loop { - let p = ExtendedPoint::random(rng).clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - - fn identity() -> Self { - SubgroupPoint(ExtendedPoint::identity()) - } - - fn generator() -> Self { - ExtendedPoint::generator().clear_cofactor() - } - - fn is_identity(&self) -> Choice { - self.0.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - SubgroupPoint(self.0.double()) - } -} - -impl WnafGroup for ExtendedPoint { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - // Copied from bls12_381::g1, should be updated. - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for SubgroupPoint {} - -impl CofactorGroup for ExtendedPoint { - type Subgroup = SubgroupPoint; - - fn clear_cofactor(&self) -> Self::Subgroup { - SubgroupPoint(self.mul_by_cofactor()) - } - - fn into_subgroup(self) -> CtOption { - CtOption::new(SubgroupPoint(self), self.is_torsion_free()) - } -} - -impl Curve for ExtendedPoint { - type AffineRepr = AffinePoint; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl CofactorCurve for ExtendedPoint { - type Affine = AffinePoint; -} - -impl CofactorCurveAffine for AffinePoint { - type Scalar = Fr; - type Curve = ExtendedPoint; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - // The point with the lowest positive v-coordinate and positive u-coordinate. - AffinePoint { - u: Fq::from_raw([ - 0xe4b3_d35d_f1a7_adfe, - 0xcaf5_5d1b_29bf_81af, - 0x8b0f_03dd_d60a_8187, - 0x62ed_cbb8_bf37_87c8, - ]), - v: Fq::from_raw([ - 0x0000_0000_0000_000b, - 0x0000_0000_0000_0000, - 0x0000_0000_0000_0000, - 0x0000_0000_0000_0000, - ]), - } - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - (*self).into() - } -} - -impl GroupEncoding for ExtendedPoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - AffinePoint::from_bytes(*bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - // We can't avoid curve checks when parsing a compressed encoding. - AffinePoint::from_bytes(*bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - AffinePoint::from(self).to_bytes() - } -} - -impl GroupEncoding for SubgroupPoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint) - } - - fn to_bytes(&self) -> Self::Repr { - self.0.to_bytes() - } -} - -impl GroupEncoding for AffinePoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_bytes(*bytes) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_bytes(*bytes) - } - - fn to_bytes(&self) -> Self::Repr { - self.to_bytes() - } -} - -#[test] -fn test_is_on_curve_var() { - assert!(AffinePoint::identity().is_on_curve_vartime()); -} - -#[test] -fn test_d_is_non_quadratic_residue() { - assert!(bool::from(EDWARDS_D.sqrt().is_none())); - assert!(bool::from((-EDWARDS_D).sqrt().is_none())); - assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none())); -} - -#[test] -fn test_affine_niels_point_identity() { - assert_eq!( - AffineNielsPoint::identity().v_plus_u, - AffinePoint::identity().to_niels().v_plus_u - ); - assert_eq!( - AffineNielsPoint::identity().v_minus_u, - AffinePoint::identity().to_niels().v_minus_u - ); - assert_eq!( - AffineNielsPoint::identity().t2d, - AffinePoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_extended_niels_point_identity() { - assert_eq!( - ExtendedNielsPoint::identity().v_plus_u, - ExtendedPoint::identity().to_niels().v_plus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().v_minus_u, - ExtendedPoint::identity().to_niels().v_minus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().z, - ExtendedPoint::identity().to_niels().z - ); - assert_eq!( - ExtendedNielsPoint::identity().t2d, - ExtendedPoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_assoc() { - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert!(p.is_on_curve_vartime()); - - assert_eq!( - (p * Fr::from(1000u64)) * Fr::from(3938u64), - p * (Fr::from(1000u64) * Fr::from(3938u64)), - ); -} - -#[test] -fn test_batch_normalize() { - let mut p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - - let mut v = vec![]; - for _ in 0..10 { - v.push(p); - p = p.double(); - } - - for p in &v { - assert!(p.is_on_curve_vartime()); - } - - let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); - let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result1[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } - let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result2[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } -} - -#[cfg(test)] -const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xe4b3_d35d_f1a7_adfe, - 0xcaf5_5d1b_29bf_81af, - 0x8b0f_03dd_d60a_8187, - 0x62ed_cbb8_bf37_87c8, - ]), - Fq::from_raw([0xb, 0x0, 0x0, 0x0]), -); - -#[cfg(test)] -const EIGHT_TORSION: [AffinePoint; 8] = [ - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xfffe_ffff_0000_0001, - 0x67ba_a400_89fb_5bfe, - 0xa5e8_0b39_939e_d334, - 0x73ed_a753_299d_7d47, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x0001_0000_0000_0000, - 0xec03_0002_7603_0000, - 0x8d51_ccce_7603_04d0, - 0x0, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([0x1, 0x0, 0x0, 0x0]), - ), -]; - -#[test] -fn find_eight_torsion() { - let g = ExtendedPoint::from(FULL_GENERATOR); - assert!(!bool::from(g.is_small_order())); - let g = g.multiply(&FR_MODULUS_BYTES); - assert!(bool::from(g.is_small_order())); - - let mut cur = g; - - for (i, point) in EIGHT_TORSION.iter().enumerate() { - let tmp = AffinePoint::from(cur); - if &tmp != point { - panic!("{}th torsion point should be {:?}", i, tmp); - } - - cur += &g; - } -} - -#[test] -fn find_curve_generator() { - let mut trial_bytes = [0; 32]; - for _ in 0..255 { - let a = AffinePoint::from_bytes(trial_bytes); - if bool::from(a.is_some()) { - let a = a.unwrap(); - assert!(a.is_on_curve_vartime()); - let b = ExtendedPoint::from(a); - let b = b.multiply(&FR_MODULUS_BYTES); - assert!(bool::from(b.is_small_order())); - let b = b.double(); - assert!(bool::from(b.is_small_order())); - let b = b.double(); - assert!(bool::from(b.is_small_order())); - if !bool::from(b.is_identity()) { - let b = b.double(); - assert!(bool::from(b.is_small_order())); - assert!(bool::from(b.is_identity())); - assert_eq!(FULL_GENERATOR, a); - assert_eq!(AffinePoint::generator(), a); - assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); - return; - } - } - - trial_bytes[0] += 1; - } - - panic!("should have found a generator of the curve"); -} - -#[test] -fn test_small_order() { - for point in EIGHT_TORSION.iter() { - assert!(bool::from(point.is_small_order())); - } -} - -#[test] -fn test_is_identity() { - let a = EIGHT_TORSION[0].mul_by_cofactor(); - let b = EIGHT_TORSION[1].mul_by_cofactor(); - - assert_eq!(a.u, b.u); - assert_eq!(a.v, a.z); - assert_eq!(b.v, b.z); - assert!(a.v != b.v); - assert!(a.z != b.z); - - assert!(bool::from(a.is_identity())); - assert!(bool::from(b.is_identity())); - - for point in EIGHT_TORSION.iter() { - assert!(bool::from(point.mul_by_cofactor().is_identity())); - } -} - -#[test] -fn test_mul_consistency() { - let a = Fr([ - 0x21e6_1211_d993_4f2e, - 0xa52c_058a_693c_3e07, - 0x9ccb_77bf_b12d_6360, - 0x07df_2470_ec94_398e, - ]); - let b = Fr([ - 0x0333_6d1c_be19_dbe0, - 0x0153_618f_6156_a536, - 0x2604_c9e1_fc3c_6b15, - 0x04ae_581c_eb02_8720, - ]); - let c = Fr([ - 0xd7ab_f5bb_2468_3f4c, - 0x9d77_12cc_274b_7c03, - 0x9732_93db_9683_789f, - 0x0b67_7e29_380a_97a7, - ]); - assert_eq!(a * b, c); - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert_eq!(p * c, (p * a) * b); - - // Test Mul implemented on ExtendedNielsPoint - assert_eq!(p * c, (p.to_niels() * a) * b); - assert_eq!(p.to_niels() * c, (p * a) * b); - assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); - - // Test Mul implemented on AffineNielsPoint - let p_affine_niels = AffinePoint::from(p).to_niels(); - assert_eq!(p * c, (p_affine_niels * a) * b); - assert_eq!(p_affine_niels * c, (p * a) * b); - assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); -} - -#[test] -fn test_serialization_consistency() { - let gen = FULL_GENERATOR.mul_by_cofactor(); - let mut p = gen; - - let v = vec![ - [ - 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, - 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, - ], - [ - 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, - 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, - ], - [ - 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, - 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, - ], - [ - 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, - 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, - ], - [ - 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, - 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, - ], - [ - 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, - 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, - ], - [ - 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, - 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, - ], - [ - 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, - 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, - ], - [ - 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, - 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, - ], - [ - 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, - 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, - ], - [ - 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, - 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, - ], - [ - 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, - 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, - ], - [ - 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, - 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, - ], - [ - 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, - 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, - ], - [ - 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, - 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, - ], - [ - 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, - 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, - ], - ]; - - for expected_serialized in v { - assert!(p.is_on_curve_vartime()); - let affine = AffinePoint::from(p); - let serialized = affine.to_bytes(); - let deserialized = AffinePoint::from_bytes(serialized).unwrap(); - assert_eq!(affine, deserialized); - assert_eq!(expected_serialized, serialized); - p += gen; - } -} diff --git a/jubjub/src/util.rs b/jubjub/src/util.rs deleted file mode 100644 index bd25dd06a2..0000000000 --- a/jubjub/src/util.rs +++ /dev/null @@ -1,174 +0,0 @@ -/// Compute a + b + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + (b as u128) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a - (b + borrow), returning the result and the new borrow. -#[inline(always)] -pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { - let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a + (b * c) + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -macro_rules! impl_add_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: &'b $rhs) -> $output { - &self + rhs - } - } - - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - &self + &rhs - } - } - }; -} - -macro_rules! impl_sub_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Sub<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: &'b $rhs) -> $output { - &self - rhs - } - } - - impl<'a> Sub<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - self - &rhs - } - } - - impl Sub<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - &self - &rhs - } - } - }; -} - -macro_rules! impl_binops_additive_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl_add_binop_specify_output!($lhs, $rhs, $output); - impl_sub_binop_specify_output!($lhs, $rhs, $output); - }; -} - -macro_rules! impl_binops_multiplicative_mixed { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: &'b $rhs) -> $output { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - &self * &rhs - } - } - }; -} - -macro_rules! impl_binops_additive { - ($lhs:ident, $rhs:ident) => { - impl_binops_additive_specify_output!($lhs, $rhs, $lhs); - - impl SubAssign<$rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: $rhs) { - *self = &*self - &rhs; - } - } - - impl AddAssign<$rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: $rhs) { - *self = &*self + &rhs; - } - } - - impl<'b> SubAssign<&'b $rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: &'b $rhs) { - *self = &*self - rhs; - } - } - - impl<'b> AddAssign<&'b $rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: &'b $rhs) { - *self = &*self + rhs; - } - } - }; -} - -macro_rules! impl_binops_multiplicative { - ($lhs:ident, $rhs:ident) => { - impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); - - impl MulAssign<$rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: $rhs) { - *self = &*self * &rhs; - } - } - - impl<'b> MulAssign<&'b $rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: &'b $rhs) { - *self = &*self * rhs; - } - } - }; -} diff --git a/jubjub/tests/common.rs b/jubjub/tests/common.rs deleted file mode 100644 index a4535edbe3..0000000000 --- a/jubjub/tests/common.rs +++ /dev/null @@ -1,29 +0,0 @@ -use jubjub::*; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub const NUM_BLACK_BOX_CHECKS: u32 = 2000; - -pub fn new_rng() -> XorShiftRng { - XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) -} - -pub trait MyRandom { - fn new_random(rng: &mut T) -> Self; -} - -impl MyRandom for Fq { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fq::from_bytes_wide(&random_bytes) - } -} - -impl MyRandom for Fr { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fr::from_bytes_wide(&random_bytes) - } -} diff --git a/jubjub/tests/fq_blackbox.rs b/jubjub/tests/fq_blackbox.rs deleted file mode 100644 index a823c9b0c5..0000000000 --- a/jubjub/tests/fq_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a + Fq::zero()); - assert_eq!(a, Fq::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a - Fq::zero()); - assert_eq!(a, Fq::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fq::zero(), a + a_neg); - assert_eq!(Fq::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a * Fq::one()); - assert_eq!(a, Fq::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - if a == Fq::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fq::one(), a * a_inv); - assert_eq!(Fq::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(Fq::zero(), Fq::zero() * a); - assert_eq!(Fq::zero(), a * Fq::zero()); - } -} diff --git a/jubjub/tests/fr_blackbox.rs b/jubjub/tests/fr_blackbox.rs deleted file mode 100644 index 6e36d0f85f..0000000000 --- a/jubjub/tests/fr_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a + Fr::zero()); - assert_eq!(a, Fr::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a - Fr::zero()); - assert_eq!(a, Fr::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fr::zero(), a + a_neg); - assert_eq!(Fr::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a * Fr::one()); - assert_eq!(a, Fr::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - if a == Fr::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fr::one(), a * a_inv); - assert_eq!(Fr::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(Fr::zero(), Fr::zero() * a); - assert_eq!(Fr::zero(), a * Fr::zero()); - } -} diff --git a/pairing/.gitignore b/pairing/.gitignore deleted file mode 100644 index 4308d82204..0000000000 --- a/pairing/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/pairing/COPYRIGHT b/pairing/COPYRIGHT deleted file mode 100644 index c3876a4d72..0000000000 --- a/pairing/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "pairing" library are retained by their contributors. No -copyright assignment is required to contribute to the "pairing" library. - -The "pairing" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml deleted file mode 100644 index 5993a58874..0000000000 --- a/pairing/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "pairing" - -# Remember to change version string in README.md. -version = "0.17.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -readme = "README.md" -license = "MIT/Apache-2.0" - -description = "Pairing-friendly elliptic curve library" -documentation = "https://docs.rs/pairing/" -homepage = "https://github.com/ebfull/pairing" -repository = "https://github.com/ebfull/pairing" -edition ="2018" - -[dependencies] -byteorder = "1" -ff = { version = "0.7", path = "../ff", features = ["derive"] } -group = { version = "0.7", path = "../group" } -rand_core = "0.5" -subtle = "2.2.1" - -[dev-dependencies] -criterion = "0.3" -rand_xorshift = "0.2" - -[features] -unstable-features = ["expose-arith"] -expose-arith = [] -default = [] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/pairing/LICENSE-APACHE b/pairing/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/pairing/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/pairing/LICENSE-MIT b/pairing/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/pairing/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/pairing/README.md b/pairing/README.md deleted file mode 100644 index e46fc58c3f..0000000000 --- a/pairing/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # - -`pairing` is a crate for using pairing-friendly elliptic curves. - -`pairing` provides basic traits for pairing-friendly elliptic curve constructions. -Specific curves are implemented in separate crates: - -- [`bls12_381`](https://crates.io/crates/bls12_381) - the BLS12-381 curve. - -## [Documentation](https://docs.rs/pairing/) - -Bring the `pairing` crate into your project just as you normally would. - -## Security Warnings - -This library does not make any guarantees about constant-time operations, memory -access patterns, or resistance to side-channel attacks. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs deleted file mode 100644 index c32d9d4850..0000000000 --- a/pairing/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! A library for working with pairing-friendly curves. - -// `clippy` is a code linting tool for improving code quality by catching -// common mistakes or strange code patterns. If the `cargo-clippy` feature -// is provided, all compiler warnings are prohibited. -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -// Force public structures to implement Debug -#![deny(missing_debug_implementations)] - -#[cfg(test)] -pub mod tests; - -use core::ops::Mul; -use ff::PrimeField; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine}, - Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, -}; - -/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) -/// with well-defined relationships. In particular, the G1/G2 curve groups are -/// of prime order `r`, and are equipped with a bilinear pairing function. -pub trait Engine: Sized + 'static + Clone { - /// This is the scalar field of the engine's groups. - type Fr: PrimeField; - - /// The projective representation of an element in G1. - type G1: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned; - - /// The affine representation of an element in G1. - type G1Affine: PairingCurveAffine< - Scalar = Self::Fr, - Curve = Self::G1, - Pair = Self::G2Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; - - /// The projective representation of an element in G2. - type G2: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned; - - /// The affine representation of an element in G2. - type G2Affine: PairingCurveAffine< - Scalar = Self::Fr, - Curve = Self::G2, - Pair = Self::G1Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; - - /// The extension field that hosts the target group of the pairing. - type Gt: Group + ScalarMul + ScalarMulOwned; - - /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and - /// other optimizations. - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; -} - -/// Affine representation of an elliptic curve point that can be used -/// to perform pairings. -pub trait PairingCurveAffine: PrimeCurveAffine + UncompressedEncoding { - type Pair: PairingCurveAffine; - type PairingResult: Group; - - /// Perform a pairing - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; -} - -/// An engine that can compute sums of pairings in an efficient way. -pub trait MultiMillerLoop: Engine { - /// The prepared form of `Self::G2Affine`. - type G2Prepared: Clone + Send + Sync + From; - - /// The type returned by `Engine::miller_loop`. - type Result: MillerLoopResult; - - /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms - /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result; -} - -/// Represents results of a Miller loop, one of the most expensive portions of the pairing -/// function. -/// -/// `MillerLoopResult`s cannot be compared with each other until -/// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. -pub trait MillerLoopResult { - /// The extension field that hosts the target group of the pairing. - type Gt: Group; - - /// This performs a "final exponentiation" routine to convert the result of a Miller - /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with - /// other elements of `Gt`. - fn final_exponentiation(&self) -> Self::Gt; -} diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs deleted file mode 100644 index 1d03784216..0000000000 --- a/pairing/src/tests/engine.rs +++ /dev/null @@ -1,130 +0,0 @@ -use ff::Field; -use group::{prime::PrimeCurveAffine, Curve, Group}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::Mul; - -use crate::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; - -pub fn engine_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..10 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - assert!(a.pairing_with(&b) == b.pairing_with(&a)); - assert!(a.pairing_with(&b) == E::pairing(&a, &b)); - } - - for _ in 0..1000 { - let z1 = E::G1Affine::identity(); - let z2 = E::G2Affine::identity().into(); - - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine().into(); - let c = E::G1::random(&mut rng).to_affine(); - let d = E::G2::random(&mut rng).to_affine().into(); - - assert_eq!( - E::Gt::identity(), - E::multi_miller_loop(&[(&z1, &b)]).final_exponentiation() - ); - - assert_eq!( - E::Gt::identity(), - E::multi_miller_loop(&[(&a, &z2)]).final_exponentiation() - ); - - assert_eq!( - E::multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), - E::multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() - ); - - assert_eq!( - E::multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), - E::multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() - ); - } - - random_bilinearity_tests::(); - random_miller_loop_tests::(); -} - -fn random_miller_loop_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // Exercise the miller loop for a reduced pairing - for _ in 0..1000 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - let p2 = E::pairing(&a, &b); - - let a = a; - let b = b.into(); - - let p1 = E::multi_miller_loop(&[(&a, &b)]).final_exponentiation(); - - assert_eq!(p1, p2); - } - - // Exercise a double miller loop - for _ in 0..1000 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - let c = E::G1::random(&mut rng).to_affine(); - let d = E::G2::random(&mut rng).to_affine(); - - let ab = E::pairing(&a, &b); - let cd = E::pairing(&c, &d); - - let abcd = ab + &cd; - - let a = a; - let b = b.into(); - let c = c; - let d = d.into(); - - let abcd_with_double_loop = - E::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); - - assert_eq!(abcd, abcd_with_double_loop); - } -} - -fn random_bilinearity_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - let c = E::Fr::random(&mut rng); - let d = E::Fr::random(&mut rng); - - let ac = (a * &c).to_affine(); - let ad = (a * &d).to_affine(); - let bc = (b * &c).to_affine(); - let bd = (b * &d).to_affine(); - - let acbd = E::pairing(&ac, &bd); - let adbc = E::pairing(&ad, &bc); - - let ab = E::pairing(&a, &b); - let cd = c * &d; - let abcd = Mul::::mul(ab, cd); - - assert_eq!(acbd, adbc); - assert_eq!(acbd, abcd); - } -} diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs deleted file mode 100644 index eb2c8fee62..0000000000 --- a/pairing/src/tests/field.rs +++ /dev/null @@ -1,243 +0,0 @@ -use ff::{Field, PrimeField}; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub fn random_sqrt_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..10000 { - let a = F::random(&mut rng); - let b = a.square(); - - let b = b.sqrt().unwrap(); - let negb = b.neg(); - - assert!(a == b || a == negb); - } - - let mut c = F::one(); - for _ in 0..10000 { - let mut b = c.square(); - - b = b.sqrt().unwrap(); - - if b != c { - b = b.neg(); - } - - assert_eq!(b, c); - - c.add_assign(&F::one()); - } -} - -pub fn random_field_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - random_multiplication_tests::(&mut rng); - random_addition_tests::(&mut rng); - random_subtraction_tests::(&mut rng); - random_negation_tests::(&mut rng); - random_doubling_tests::(&mut rng); - random_squaring_tests::(&mut rng); - random_inversion_tests::(&mut rng); - random_expansion_tests::(&mut rng); - - assert!(F::zero().is_zero()); - { - let z = F::zero().neg(); - assert!(z.is_zero()); - } - - assert!(bool::from(F::zero().invert().is_none())); - - // Multiplication by zero - { - let mut a = F::random(&mut rng); - a.mul_assign(&F::zero()); - assert!(a.is_zero()); - } - - // Addition by zero - { - let mut a = F::random(&mut rng); - let copy = a; - a.add_assign(&F::zero()); - assert_eq!(a, copy); - } -} - -pub fn from_str_tests() { - { - let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234"; - let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039"; - let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126"; - - let mut a = F::from_str(a).unwrap(); - let b = F::from_str(b).unwrap(); - let c = F::from_str(c).unwrap(); - - a.mul_assign(&b); - - assert_eq!(a, c); - } - - { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let n = rng.next_u64(); - - let a = F::from_str(&format!("{}", n)).unwrap(); - let b = F::from(n); - - assert_eq!(a, b); - } - } - - assert!(F::from_str("").is_none()); - assert!(F::from_str("0").unwrap().is_zero()); - assert!(F::from_str("00").is_none()); - assert!(F::from_str("00000000000").is_none()); -} - -fn random_multiplication_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - - let mut t0 = a; // (a * b) * c - t0.mul_assign(&b); - t0.mul_assign(&c); - - let mut t1 = a; // (a * c) * b - t1.mul_assign(&c); - t1.mul_assign(&b); - - let mut t2 = b; // (b * c) * a - t2.mul_assign(&c); - t2.mul_assign(&a); - - assert_eq!(t0, t1); - assert_eq!(t1, t2); - } -} - -fn random_addition_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - - let mut t0 = a; // (a + b) + c - t0.add_assign(&b); - t0.add_assign(&c); - - let mut t1 = a; // (a + c) + b - t1.add_assign(&c); - t1.add_assign(&b); - - let mut t2 = b; // (b + c) + a - t2.add_assign(&c); - t2.add_assign(&a); - - assert_eq!(t0, t1); - assert_eq!(t1, t2); - } -} - -fn random_subtraction_tests(rng: &mut R) { - for _ in 0..10000 { - let b = F::random(rng); - let a = F::random(rng); - - let mut t0 = a; // (a - b) - t0.sub_assign(&b); - - let mut t1 = b; // (b - a) - t1.sub_assign(&a); - - let mut t2 = t0; // (a - b) + (b - a) = 0 - t2.add_assign(&t1); - - assert!(t2.is_zero()); - } -} - -fn random_negation_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let mut b = a.neg(); - b.add_assign(&a); - - assert!(b.is_zero()); - } -} - -fn random_doubling_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a + a, a.double()); - } -} - -fn random_squaring_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a * a, a.square()); - } -} - -fn random_inversion_tests(rng: &mut R) { - assert!(bool::from(F::zero().invert().is_none())); - - for _ in 0..10000 { - let mut a = F::random(rng); - let b = a.invert().unwrap(); // probablistically nonzero - a.mul_assign(&b); - - assert_eq!(a, F::one()); - } -} - -fn random_expansion_tests(rng: &mut R) { - for _ in 0..10000 { - // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) - - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - let d = F::random(rng); - - let mut t0 = a; - t0.add_assign(&b); - let mut t1 = c; - t1.add_assign(&d); - t0.mul_assign(&t1); - - let mut t2 = a; - t2.mul_assign(&c); - let mut t3 = b; - t3.mul_assign(&c); - let mut t4 = a; - t4.mul_assign(&d); - let mut t5 = b; - t5.mul_assign(&d); - - t2.add_assign(&t3); - t2.add_assign(&t4); - t2.add_assign(&t5); - - assert_eq!(t0, t2); - } -} diff --git a/pairing/src/tests/mod.rs b/pairing/src/tests/mod.rs deleted file mode 100644 index d6ad6a12f1..0000000000 --- a/pairing/src/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod engine; -pub mod field; -pub mod repr; diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs deleted file mode 100644 index bdaffaa83c..0000000000 --- a/pairing/src/tests/repr.rs +++ /dev/null @@ -1,23 +0,0 @@ -use ff::PrimeField; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; - -pub fn random_repr_tests() { - random_encoding_tests::

(); -} - -fn random_encoding_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let r = P::random(&mut rng); - - let v = r.to_repr(); - let rdecoded = P::from_repr(v).unwrap(); - - assert_eq!(r, rdecoded); - } -} From 25634e90e7b6426a04c3a3b9cd02dc6b463f3911 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 Apr 2020 15:44:09 +1200 Subject: [PATCH 195/210] Core TZE traits and structs Co-authored-by: Kris Nuttycombe --- Cargo.toml | 1 + zcash_extensions_api/Cargo.toml | 11 +++ zcash_extensions_api/src/lib.rs | 1 + zcash_extensions_api/src/transparent.rs | 107 ++++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 zcash_extensions_api/Cargo.toml create mode 100644 zcash_extensions_api/src/lib.rs create mode 100644 zcash_extensions_api/src/transparent.rs diff --git a/Cargo.toml b/Cargo.toml index b1943e06ac..3ab4b6c3ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "components/equihash", "zcash_client_backend", "zcash_client_sqlite", + "zcash_extensions_api", "zcash_history", "zcash_primitives", "zcash_proofs", diff --git a/zcash_extensions_api/Cargo.toml b/zcash_extensions_api/Cargo.toml new file mode 100644 index 0000000000..2deb5dadf4 --- /dev/null +++ b/zcash_extensions_api/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "zcash_extensions_api" +description = "TBD" +version = "0.0.0" +authors = ["Jack Grigg "] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] diff --git a/zcash_extensions_api/src/lib.rs b/zcash_extensions_api/src/lib.rs new file mode 100644 index 0000000000..7d540a8bac --- /dev/null +++ b/zcash_extensions_api/src/lib.rs @@ -0,0 +1 @@ +pub mod transparent; diff --git a/zcash_extensions_api/src/transparent.rs b/zcash_extensions_api/src/transparent.rs new file mode 100644 index 0000000000..028075f192 --- /dev/null +++ b/zcash_extensions_api/src/transparent.rs @@ -0,0 +1,107 @@ +//! Core traits and structs for Transparent Zcash Extensions. + +use std::fmt; + +pub trait FromPayload: Sized { + /// Parses an extension type from a mode and payload. + fn from_payload(mode: usize, payload: &[u8]) -> Result; +} + +pub trait ToPayload { + /// Returns a serialized payload and its corresponding mode. + fn to_payload(&self) -> (usize, Vec); +} + +/// A condition that can be used to encumber transparent funds. +#[derive(Debug)] +pub struct Precondition { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Precondition { + pub fn from(extension_id: usize, value: &P) -> Precondition { + let (mode, payload) = value.to_payload(); + Precondition { + extension_id, + mode, + payload, + } + } +} + +/// Data that satisfies the precondition for prior encumbered funds, enabling them to be +/// spent. +#[derive(Debug)] +pub struct Witness { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Witness { + pub fn from(extension_id: usize, value: &P) -> Witness { + let (mode, payload) = value.to_payload(); + Witness { + extension_id, + mode, + payload, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidForEpoch(u32, usize), + InvalidExtensionId(usize), + ProgramError(E), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidForEpoch(cid, ptype) => write!( + f, + "Program type {} is invalid for consensus branch id {}", + ptype, cid + ), + + Error::InvalidExtensionId(extension_id) => { + write!(f, "Unrecognized program type id {}", extension_id) + } + + Error::ProgramError(err) => write!(f, "Program error: {}", err), + } + } +} + +pub trait Extension { + type P; + type W; + type Error; + + fn verify_inner( + &self, + precondition: &Self::P, + witness: &Self::W, + context: &C, + ) -> Result<(), Self::Error>; + + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Self::Error> + where + Self::P: FromPayload, + Self::W: FromPayload, + { + self.verify_inner( + &Self::P::from_payload(precondition.mode, &precondition.payload)?, + &Self::W::from_payload(witness.mode, &witness.payload)?, + &context, + ) + } +} From c94f1ff835ce1b97a803564ddee4144e04ef1c78 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 Nov 2019 12:12:45 +0000 Subject: [PATCH 196/210] Add TZEs to transaction format --- zcash_primitives/Cargo.toml | 1 + zcash_primitives/src/serialize.rs | 6 +- .../src/transaction/components.rs | 75 +++++++++++++++++ zcash_primitives/src/transaction/mod.rs | 84 +++++++++++++++---- 4 files changed, 149 insertions(+), 17 deletions(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 393ab55cdd..0e91459a53 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -35,6 +35,7 @@ ripemd160 = { version = "0.9", optional = true } secp256k1 = { version = "0.17", optional = true } sha2 = "0.9" subtle = "2.2.1" +zcash_extensions_api = { version = "0.0", path = "../zcash_extensions_api" } [dev-dependencies] criterion = "0.3" diff --git a/zcash_primitives/src/serialize.rs b/zcash_primitives/src/serialize.rs index 4e0fb9338c..5e56fbb3d9 100644 --- a/zcash_primitives/src/serialize.rs +++ b/zcash_primitives/src/serialize.rs @@ -3,10 +3,10 @@ use std::io::{self, Read, Write}; const MAX_SIZE: usize = 0x02000000; -struct CompactSize; +pub(crate) struct CompactSize; impl CompactSize { - fn read(mut reader: R) -> io::Result { + pub(crate) fn read(mut reader: R) -> io::Result { let flag = reader.read_u8()?; match if flag < 253 { Ok(flag as usize) @@ -43,7 +43,7 @@ impl CompactSize { } } - fn write(mut writer: W, size: usize) -> io::Result<()> { + pub(crate) fn write(mut writer: W, size: usize) -> io::Result<()> { match size { s if s < 253 => writer.write_u8(s as u8), s if s <= 0xFFFF => { diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 0929eda51f..ad0219a8f1 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -4,9 +4,11 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::PrimeField; use group::GroupEncoding; use std::io::{self, Read, Write}; +use zcash_extensions_api::transparent as tze; use crate::legacy::Script; use crate::redjubjub::{PublicKey, Signature}; +use crate::serialize::{CompactSize, Vector}; pub mod amount; pub use self::amount::Amount; @@ -116,6 +118,79 @@ impl TxOut { } } +#[derive(Debug)] +pub struct TzeIn { + pub prevout: OutPoint, + pub witness: tze::Witness, +} + +impl TzeIn { + pub fn read(mut reader: &mut R) -> io::Result { + let prevout = OutPoint::read(&mut reader)?; + + let extension_id = CompactSize::read(&mut reader)?; + let mode = CompactSize::read(&mut reader)?; + let payload = Vector::read(&mut reader, |r| r.read_u8())?; + + Ok(TzeIn { + prevout, + witness: tze::Witness { + extension_id, + mode, + payload, + }, + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.prevout.write(&mut writer)?; + + CompactSize::write(&mut writer, self.witness.extension_id)?; + CompactSize::write(&mut writer, self.witness.mode)?; + Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b)) + } +} + +#[derive(Debug)] +pub struct TzeOut { + pub value: Amount, + pub precondition: tze::Precondition, +} + +impl TzeOut { + pub fn read(mut reader: &mut R) -> io::Result { + let value = { + let mut tmp = [0; 8]; + reader.read_exact(&mut tmp)?; + Amount::from_nonnegative_i64_le_bytes(tmp) + } + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?; + + let extension_id = CompactSize::read(&mut reader)?; + let mode = CompactSize::read(&mut reader)?; + let payload = Vector::read(&mut reader, |r| r.read_u8())?; + + Ok(TzeOut { + value, + precondition: tze::Precondition { + extension_id, + mode, + payload, + }, + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.value.to_i64_le_bytes())?; + + CompactSize::write(&mut writer, self.precondition.extension_id)?; + CompactSize::write(&mut writer, self.precondition.mode)?; + Vector::write(&mut writer, &self.precondition.payload, |w, b| { + w.write_u8(*b) + }) + } +} + pub struct SpendDescription { pub cv: jubjub::ExtendedPoint, pub anchor: bls12_381::Scalar, diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 24b812703c..42cf8fc31a 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -19,12 +19,16 @@ mod tests; pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL}; -use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut}; +use self::components::{ + Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut, +}; const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270; const OVERWINTER_TX_VERSION: u32 = 3; const SAPLING_VERSION_GROUP_ID: u32 = 0x892F2085; const SAPLING_TX_VERSION: u32 = 4; +const FUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF; +const FUTURE_TX_VERSION: u32 = 0x0000FFFF; #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct TxId(pub [u8; 32]); @@ -64,6 +68,8 @@ pub struct TransactionData { pub version_group_id: u32, pub vin: Vec, pub vout: Vec, + pub tze_inputs: Vec, + pub tze_outputs: Vec, pub lock_time: u32, pub expiry_height: u32, pub value_balance: Amount, @@ -85,6 +91,8 @@ impl std::fmt::Debug for TransactionData { version_group_id = {:?}, vin = {:?}, vout = {:?}, + tze_inputs = {:?}, + tze_outputs = {:?}, lock_time = {:?}, expiry_height = {:?}, value_balance = {:?}, @@ -98,6 +106,8 @@ impl std::fmt::Debug for TransactionData { self.version_group_id, self.vin, self.vout, + self.tze_inputs, + self.tze_outputs, self.lock_time, self.expiry_height, self.value_balance, @@ -118,6 +128,29 @@ impl TransactionData { version_group_id: SAPLING_VERSION_GROUP_ID, vin: vec![], vout: vec![], + tze_inputs: vec![], + tze_outputs: vec![], + lock_time: 0, + expiry_height: 0, + value_balance: Amount::zero(), + shielded_spends: vec![], + shielded_outputs: vec![], + joinsplits: vec![], + joinsplit_pubkey: None, + joinsplit_sig: None, + binding_sig: None, + } + } + + pub fn nu4() -> Self { + TransactionData { + overwintered: true, + version: FUTURE_TX_VERSION, + version_group_id: FUTURE_VERSION_GROUP_ID, + vin: vec![], + vout: vec![], + tze_inputs: vec![], + tze_outputs: vec![], lock_time: 0, expiry_height: 0, value_balance: Amount::zero(), @@ -178,7 +211,11 @@ impl Transaction { let is_sapling_v4 = overwintered && version_group_id == SAPLING_VERSION_GROUP_ID && version == SAPLING_TX_VERSION; - if overwintered && !(is_overwinter_v3 || is_sapling_v4) { + let has_tze = overwintered + && version_group_id == FUTURE_VERSION_GROUP_ID + && version == FUTURE_TX_VERSION; + + if overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Unknown transaction format", @@ -187,14 +224,21 @@ impl Transaction { let vin = Vector::read(&mut reader, TxIn::read)?; let vout = Vector::read(&mut reader, TxOut::read)?; + let (tze_inputs, tze_outputs) = if has_tze { + let wi = Vector::read(&mut reader, TzeIn::read)?; + let wo = Vector::read(&mut reader, TzeOut::read)?; + (wi, wo) + } else { + (vec![], vec![]) + }; let lock_time = reader.read_u32::()?; - let expiry_height = if is_overwinter_v3 || is_sapling_v4 { + let expiry_height = if is_overwinter_v3 || is_sapling_v4 || has_tze { reader.read_u32::()? } else { 0 }; - let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 { + let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 || has_tze { let vb = { let mut tmp = [0; 8]; reader.read_exact(&mut tmp)?; @@ -226,12 +270,13 @@ impl Transaction { (vec![], None, None) }; - let binding_sig = - if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { - Some(Signature::read(&mut reader)?) - } else { - None - }; + let binding_sig = if (is_sapling_v4 || has_tze) + && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) + { + Some(Signature::read(&mut reader)?) + } else { + None + }; Transaction::from_data(TransactionData { overwintered, @@ -239,6 +284,8 @@ impl Transaction { version_group_id, vin, vout, + tze_inputs, + tze_outputs, lock_time, expiry_height, value_balance, @@ -263,7 +310,10 @@ impl Transaction { let is_sapling_v4 = self.overwintered && self.version_group_id == SAPLING_VERSION_GROUP_ID && self.version == SAPLING_TX_VERSION; - if self.overwintered && !(is_overwinter_v3 || is_sapling_v4) { + let is_nu4_v5 = self.overwintered + && self.version_group_id == FUTURE_VERSION_GROUP_ID + && self.version == FUTURE_TX_VERSION; + if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || is_nu4_v5) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Unknown transaction format", @@ -272,12 +322,16 @@ impl Transaction { Vector::write(&mut writer, &self.vin, |w, e| e.write(w))?; Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?; + if is_nu4_v5 { + Vector::write(&mut writer, &self.tze_inputs, |w, e| e.write(w))?; + Vector::write(&mut writer, &self.tze_outputs, |w, e| e.write(w))?; + } writer.write_u32::(self.lock_time)?; - if is_overwinter_v3 || is_sapling_v4 { + if is_overwinter_v3 || is_sapling_v4 || is_nu4_v5 { writer.write_u32::(self.expiry_height)?; } - if is_sapling_v4 { + if is_sapling_v4 || is_nu4_v5 { writer.write_all(&self.value_balance.to_i64_le_bytes())?; Vector::write(&mut writer, &self.shielded_spends, |w, e| e.write(w))?; Vector::write(&mut writer, &self.shielded_outputs, |w, e| e.write(w))?; @@ -322,7 +376,9 @@ impl Transaction { } } - if is_sapling_v4 && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) { + if (is_sapling_v4 || is_nu4_v5) + && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) + { match self.binding_sig { Some(sig) => sig.write(&mut writer)?, None => { From 2db7f6d5a77a65bc6f07f2c5cc3917e6a0ae231f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 Nov 2019 12:44:59 +0000 Subject: [PATCH 197/210] Demo TZE parsing and consensus logic --- zcash_primitives/src/extensions.rs | 3 + .../src/extensions/transparent.rs | 3 + .../src/extensions/transparent/demo.rs | 436 ++++++++++++++++++ zcash_primitives/src/lib.rs | 1 + 4 files changed, 443 insertions(+) create mode 100644 zcash_primitives/src/extensions.rs create mode 100644 zcash_primitives/src/extensions/transparent.rs create mode 100644 zcash_primitives/src/extensions/transparent/demo.rs diff --git a/zcash_primitives/src/extensions.rs b/zcash_primitives/src/extensions.rs new file mode 100644 index 0000000000..1aa5804f34 --- /dev/null +++ b/zcash_primitives/src/extensions.rs @@ -0,0 +1,3 @@ +//! Zcash extensions. + +pub mod transparent; diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs new file mode 100644 index 0000000000..48aca679f4 --- /dev/null +++ b/zcash_primitives/src/extensions/transparent.rs @@ -0,0 +1,3 @@ +//! Zcash transparent extensions. + +pub mod demo; diff --git a/zcash_primitives/src/extensions/transparent/demo.rs b/zcash_primitives/src/extensions/transparent/demo.rs new file mode 100644 index 0000000000..cadd020dfd --- /dev/null +++ b/zcash_primitives/src/extensions/transparent/demo.rs @@ -0,0 +1,436 @@ +//! Demo implementation of TZE consensus rules. +//! +//! The demo program implements a dual-hash-lock encumbrance with the following form: +//! +//! > `hash = BLAKE2b_256(preimage_1 || BLAKE2b_256(preimage_2))` +//! +//! The two preimages are revealed in sequential transactions, demonstrating how TZEs can +//! impose constraints on how program modes are chained together. +//! +//! The demo program has two modes: +//! +//! - Mode 0: `hash_1 = BLAKE2b_256(preimage_1 || hash_2)` +//! - Mode 1: `hash_2 = BLAKE2b_256(preimage_2)` +//! +//! and uses the following transaction formats: +//! +//! - `tx_a`: `[ [any input types...] ----> TzeOut(value, hash_1) ]` +//! - `tx_b`: `[ TzeIn(tx_a, preimage_1) -> TzeOut(value, hash_2) ]` +//! - `tx_c`: `[ TzeIn(tx_b, preimage_2) -> [any output types...] ]` + +use blake2b_simd::Params; +use std::convert::TryFrom; +use std::fmt; +use zcash_extensions_api::transparent::{Extension, FromPayload, ToPayload}; + +use crate::transaction::components::TzeOut; + +mod open { + pub const MODE: usize = 0; + + #[derive(Debug, PartialEq)] + pub struct Precondition(pub [u8; 32]); + + #[derive(Debug, PartialEq)] + pub struct Witness(pub [u8; 32]); +} + +mod close { + pub const MODE: usize = 1; + + #[derive(Debug, PartialEq)] + pub struct Precondition(pub [u8; 32]); + + #[derive(Debug, PartialEq)] + pub struct Witness(pub [u8; 32]); +} + +#[derive(Debug, PartialEq)] +pub enum Precondition { + Open(open::Precondition), + Close(close::Precondition), +} + +impl Precondition { + pub fn open(hash: [u8; 32]) -> Self { + Precondition::Open(open::Precondition(hash)) + } + + pub fn close(hash: [u8; 32]) -> Self { + Precondition::Close(close::Precondition(hash)) + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + IllegalPayloadLength(usize), + ModeInvalid(usize), + NonTzeTxn, + HashMismatch, // include hashes? + ModeMismatch, + ExpectedClose, + InvalidOutputQty(usize), +} + +impl fmt::Display for Error { + fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result { + match self { + Error::IllegalPayloadLength(sz) => write!(f, "Illegal payload length for demo: {}", sz), + Error::ModeInvalid(m) => write!(f, "Invalid TZE mode for demo program: {}", m), + Error::NonTzeTxn => write!(f, "Transaction has non-TZE inputs."), + Error::HashMismatch => write!(f, "Hash mismatch"), + Error::ModeMismatch => write!(f, "Extension operation mode mismatch."), + Error::ExpectedClose => write!(f, "Got open, expected close."), + Error::InvalidOutputQty(qty) => write!(f, "Incorrect number of outputs: {}", qty), + } + } +} + +impl TryFrom<(usize, Precondition)> for Precondition { + type Error = Error; + + fn try_from(from: (usize, Self)) -> Result { + match from { + (open::MODE, Precondition::Open(p)) => Ok(Precondition::Open(p)), + (close::MODE, Precondition::Close(p)) => Ok(Precondition::Close(p)), + _ => Err(Error::ModeInvalid(from.0)), + } + } +} + +impl FromPayload for Precondition { + fn from_payload(mode: usize, payload: &[u8]) -> Result { + match mode { + open::MODE => { + if payload.len() == 32 { + let mut hash = [0; 32]; + hash.copy_from_slice(&payload); + Ok(Precondition::Open(open::Precondition(hash))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + close::MODE => { + if payload.len() == 32 { + let mut hash = [0; 32]; + hash.copy_from_slice(&payload); + Ok(Precondition::Close(close::Precondition(hash))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + _ => Err(Error::ModeInvalid(mode)), + } + } +} + +impl ToPayload for Precondition { + fn to_payload(&self) -> (usize, Vec) { + match self { + Precondition::Open(p) => (open::MODE, p.0.to_vec()), + Precondition::Close(p) => (close::MODE, p.0.to_vec()), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Witness { + Open(open::Witness), + Close(close::Witness), +} + +impl Witness { + pub fn open(preimage: [u8; 32]) -> Self { + Witness::Open(open::Witness(preimage)) + } + + pub fn close(preimage: [u8; 32]) -> Self { + Witness::Close(close::Witness(preimage)) + } +} + +impl TryFrom<(usize, Witness)> for Witness { + type Error = Error; + + fn try_from(from: (usize, Self)) -> Result { + match from { + (open::MODE, Witness::Open(p)) => Ok(Witness::Open(p)), + (close::MODE, Witness::Close(p)) => Ok(Witness::Close(p)), + _ => Err(Error::ModeInvalid(from.0)), + } + } +} + +impl FromPayload for Witness { + fn from_payload(mode: usize, payload: &[u8]) -> Result { + match mode { + open::MODE => { + if payload.len() == 32 { + let mut preimage = [0; 32]; + preimage.copy_from_slice(&payload); + Ok(Witness::Open(open::Witness(preimage))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + close::MODE => { + if payload.len() == 32 { + let mut preimage = [0; 32]; + preimage.copy_from_slice(&payload); + Ok(Witness::Close(close::Witness(preimage))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + _ => Err(Error::ModeInvalid(mode)), + } + } +} + +impl ToPayload for Witness { + fn to_payload(&self) -> (usize, Vec) { + match self { + Witness::Open(w) => (open::MODE, w.0.to_vec()), + Witness::Close(w) => (close::MODE, w.0.to_vec()), + } + } +} + +pub trait Context { + fn is_tze_only(&self) -> bool; + fn tx_tze_outputs(&self) -> &[TzeOut]; +} + +pub struct Program; + +impl Extension for Program { + type P = Precondition; + type W = Witness; + type Error = Error; + + /// Runs the program against the given precondition, witness, and context. + /// + /// At this point the precondition and witness have been parsed and validated + /// non-contextually, and are guaranteed to both be for this program. All subsequent + /// validation is this function's responsibility. + fn verify_inner( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Error> { + // This match statement is selecting the mode that the program is operating in, + // based on the enums defined in the parser. + match (precondition, witness) { + (Precondition::Open(p_open), Witness::Open(w_open)) => { + // In OPEN mode, we enforce that the transaction must only contain inputs + // and outputs from this program. The consensus rules enforce that if a + // transaction contains both TZE inputs and TZE outputs, they must all be + // of the same program type. Therefore we only need to check that the + // transaction does not contain any other type of input or output. + if !context.is_tze_only() { + return Err(Error::NonTzeTxn); + } + + // Next, check that there is only a single TZE output of the correct type. + let outputs = context.tx_tze_outputs(); + match outputs { + [tze_out] => match Precondition::from_payload( + tze_out.precondition.mode, + &tze_out.precondition.payload, + ) { + Ok(Precondition::Close(p_close)) => { + // Finally, check the precondition: + // precondition_open = BLAKE2b_256(witness_open || precondition_close) + let mut h = Params::new().hash_length(32).to_state(); + h.update(&w_open.0); + h.update(&p_close.0); + let hash = h.finalize(); + if hash.as_bytes() == p_open.0 { + Ok(()) + } else { + Err(Error::HashMismatch) + } + } + Ok(Precondition::Open(_)) => Err(Error::ExpectedClose), + Err(e) => Err(e), + }, + _ => Err(Error::InvalidOutputQty(outputs.len())), + } + } + (Precondition::Close(p), Witness::Close(w)) => { + // In CLOSE mode, we only require that the precondition is satisfied: + // precondition_close = BLAKE2b_256(witness_close) + let hash = Params::new().hash_length(32).hash(&w.0); + if hash.as_bytes() == p.0 { + Ok(()) + } else { + Err(Error::HashMismatch) + } + } + _ => Err(Error::ModeMismatch), + } + } +} + +#[cfg(test)] +mod tests { + use blake2b_simd::Params; + use zcash_extensions_api::transparent::{self as tze, Extension, FromPayload, ToPayload}; + + use super::{close, open, Context, Precondition, Program, Witness}; + use crate::transaction::{ + components::{Amount, OutPoint, TzeIn, TzeOut}, + Transaction, TransactionData, + }; + + #[test] + fn precondition_open_round_trip() { + let data = vec![7; 32]; + let p = Precondition::from_payload(open::MODE, &data).unwrap(); + assert_eq!(p, Precondition::Open(open::Precondition([7; 32]))); + assert_eq!(p.to_payload(), (open::MODE, data)); + } + + #[test] + fn precondition_close_round_trip() { + let data = vec![7; 32]; + let p = Precondition::from_payload(close::MODE, &data).unwrap(); + assert_eq!(p, Precondition::Close(close::Precondition([7; 32]))); + assert_eq!(p.to_payload(), (close::MODE, data)); + } + + #[test] + fn precondition_rejects_invalid_mode_or_length() { + for mode in 0..3 { + for len in &[31, 33] { + let p = Precondition::from_payload(mode, &vec![7; *len]); + assert!(p.is_err()); + } + } + } + + #[test] + fn witness_open_round_trip() { + let data = vec![7; 32]; + let w = Witness::from_payload(open::MODE, &data).unwrap(); + assert_eq!(w, Witness::Open(open::Witness([7; 32]))); + assert_eq!(w.to_payload(), (open::MODE, data)); + } + + #[test] + fn witness_close_round_trip() { + let data = vec![7; 32]; + let p = Witness::from_payload(close::MODE, &data).unwrap(); + assert_eq!(p, Witness::Close(close::Witness([7; 32]))); + assert_eq!(p.to_payload(), (close::MODE, data)); + } + + #[test] + fn witness_rejects_invalid_mode_or_length() { + for mode in 0..3 { + for len in &[31, 33] { + let p = Witness::from_payload(mode, &vec![7; *len]); + assert!(p.is_err()); + } + } + } + + /// Dummy context + pub struct Ctx<'a> { + pub tx: &'a Transaction, + } + + /// Implementation of required operations for the demo extension, as satisfied + /// by the context. + impl<'a> Context for Ctx<'a> { + fn is_tze_only(&self) -> bool { + self.tx.vin.is_empty() + && self.tx.vout.is_empty() + && self.tx.shielded_spends.is_empty() + && self.tx.shielded_outputs.is_empty() + && self.tx.joinsplits.is_empty() + } + + fn tx_tze_outputs(&self) -> &[TzeOut] { + &self.tx.tze_outputs + } + } + + #[test] + fn demo_program() { + let preimage_1 = [1; 32]; + let preimage_2 = [2; 32]; + + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage_2).as_bytes()); + hash + }; + + let hash_1 = { + let mut hash = [0; 32]; + hash.copy_from_slice( + Params::new() + .hash_length(32) + .to_state() + .update(&preimage_1) + .update(&hash_2) + .finalize() + .as_bytes(), + ); + hash + }; + + let mut mtx_a = TransactionData::nu4(); + mtx_a.tze_outputs.push(TzeOut { + value: Amount::from_u64(1).unwrap(), + precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), + }); + let tx_a = mtx_a.freeze().unwrap(); + + let mut mtx_b = TransactionData::nu4(); + mtx_b.tze_inputs.push(TzeIn { + prevout: OutPoint::new(tx_a.txid().0, 0), + witness: tze::Witness::from(0, &Witness::open(preimage_1)), + }); + mtx_b.tze_outputs.push(TzeOut { + value: Amount::from_u64(1).unwrap(), + precondition: tze::Precondition::from(0, &Precondition::close(hash_2)), + }); + let tx_b = mtx_b.freeze().unwrap(); + + let mut mtx_c = TransactionData::nu4(); + mtx_c.tze_inputs.push(TzeIn { + prevout: OutPoint::new(tx_b.txid().0, 0), + witness: tze::Witness::from(0, &Witness::close(preimage_2)), + }); + let tx_c = mtx_c.freeze().unwrap(); + + // Verify tx_b + { + let ctx = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_a.tze_outputs[0].precondition, + &tx_b.tze_inputs[0].witness, + &ctx + ), + Ok(()) + ); + } + + // Verify tx_c + { + let ctx = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_b.tze_outputs[0].precondition, + &tx_c.tze_inputs[0].witness, + &ctx + ), + Ok(()) + ); + } + } +} diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index b4908b578d..36fee0cd2c 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -10,6 +10,7 @@ pub mod block; pub mod consensus; pub mod constants; +pub mod extensions; pub mod group_hash; pub mod keys; pub mod legacy; From 6ab82cc70322fca1289afcae0207e0aa6ec2dc8f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 23 Apr 2020 21:38:54 -0600 Subject: [PATCH 198/210] TZE consensus context and program sets Co-authored-by: Kris Nuttycombe --- zcash_primitives/src/consensus.rs | 4 +- zcash_primitives/src/consensus/extensions.rs | 3 + .../src/consensus/extensions/transparent.rs | 109 ++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 zcash_primitives/src/consensus/extensions.rs create mode 100644 zcash_primitives/src/consensus/extensions/transparent.rs diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 4cc67f49b5..952c14aa38 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -1,8 +1,10 @@ -//! Consensus parameters. +//! Consensus logic and parameters. use std::convert::TryFrom; use std::fmt; +pub mod extensions; + /// Zcash consensus parameters. pub trait Parameters { fn activation_height(nu: NetworkUpgrade) -> Option; diff --git a/zcash_primitives/src/consensus/extensions.rs b/zcash_primitives/src/consensus/extensions.rs new file mode 100644 index 0000000000..6210d3fcdd --- /dev/null +++ b/zcash_primitives/src/consensus/extensions.rs @@ -0,0 +1,3 @@ +//! Consensus logic for Zcash extensions. + +pub mod transparent; diff --git a/zcash_primitives/src/consensus/extensions/transparent.rs b/zcash_primitives/src/consensus/extensions/transparent.rs new file mode 100644 index 0000000000..2c3e21745d --- /dev/null +++ b/zcash_primitives/src/consensus/extensions/transparent.rs @@ -0,0 +1,109 @@ +//! Consensus logic for Transparent Zcash Extensions. + +use std::convert::TryFrom; +use zcash_extensions_api::transparent::{Error, Extension, Precondition, Witness}; + +use crate::extensions::transparent::demo; +use crate::transaction::components::TzeOut; +use crate::transaction::Transaction; + +/// The set of programs that have assigned type IDs within the Zcash consensus rules. +#[derive(Debug, Clone, Copy)] +pub enum ExtensionId { + Demo, +} + +pub struct InvalidExtId(usize); + +impl TryFrom for ExtensionId { + type Error = InvalidExtId; + + fn try_from(t: usize) -> Result { + match t { + 0 => Ok(ExtensionId::Demo), + n => Err(InvalidExtId(n)), + } + } +} + +impl From for usize { + fn from(type_id: ExtensionId) -> usize { + match type_id { + ExtensionId::Demo => 0, + } + } +} + +/// The complete set of context data that is available to any extension having +/// an assigned extension type ID. +pub struct Context<'a> { + pub height: i32, + pub tx: &'a Transaction, +} + +impl<'a> Context<'a> { + pub fn new(height: i32, tx: &'a Transaction) -> Self { + Context { height, tx } + } +} + +pub trait Epoch { + type Error; + + fn verify<'a>( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &Context<'a>, + ) -> Result<(), Error>; +} + +/// Implementation of required operations for the demo extension, as satisfied +/// by the context. +impl<'a> demo::Context for Context<'a> { + fn is_tze_only(&self) -> bool { + self.tx.vin.is_empty() + && self.tx.vout.is_empty() + && self.tx.shielded_spends.is_empty() + && self.tx.shielded_outputs.is_empty() + && self.tx.joinsplits.is_empty() + } + + fn tx_tze_outputs(&self) -> &[TzeOut] { + &self.tx.tze_outputs + } +} + +/// Wire identifier for the dummy network upgrade epoch. +pub const V1_EPOCH_ID: u32 = 0x7473_6554; + +/// A set of demo TZEs associated with the dummy network upgrade. +struct EpochV1; + +impl Epoch for EpochV1 { + type Error = String; + + fn verify<'a>( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &Context<'a>, + ) -> Result<(), Error> { + // This epoch contains the following set of programs: + let ext_id = ExtensionId::try_from(precondition.extension_id) + .map_err(|InvalidExtId(id)| Error::InvalidExtensionId(id))?; + match ext_id { + ExtensionId::Demo => demo::Program + .verify(precondition, witness, ctx) + .map_err(|e| Error::ProgramError(format!("Epoch v1 program error: {}", e))), + } + } +} + +pub fn epoch_for_branch(consensus_branch_id: u32) -> Option>> { + // Map from consensus branch IDs to epochs. + match consensus_branch_id { + V1_EPOCH_ID => Some(Box::new(EpochV1)), + _ => None, + } +} From ee4bf5a78669edcd3206a694e8e9725f7879660a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 May 2020 11:52:03 +1200 Subject: [PATCH 199/210] Add transaction-builder suport for TZE-bearing transactions. --- Cargo.toml | 2 +- .../Cargo.toml | 6 +- .../src/consensus.rs | 0 .../src/consensus}/transparent.rs | 8 +- zcash_extensions/src/lib.rs | 2 + zcash_extensions/src/transparent.rs | 3 + .../src}/transparent/demo.rs | 131 +++++++++++++-- zcash_extensions_api/src/transparent.rs | 107 ------------ zcash_primitives/Cargo.toml | 1 - zcash_primitives/src/consensus.rs | 2 - zcash_primitives/src/consensus/extensions.rs | 3 - zcash_primitives/src/extensions.rs | 2 - .../src/extensions/transparent.rs | 158 +++++++++++++++++- zcash_primitives/src/transaction/builder.rs | 150 ++++++++++++++--- .../src/transaction/components.rs | 6 +- 15 files changed, 420 insertions(+), 161 deletions(-) rename {zcash_extensions_api => zcash_extensions}/Cargo.toml (53%) rename zcash_extensions_api/src/lib.rs => zcash_extensions/src/consensus.rs (100%) rename {zcash_primitives/src/consensus/extensions => zcash_extensions/src/consensus}/transparent.rs (92%) create mode 100644 zcash_extensions/src/lib.rs create mode 100644 zcash_extensions/src/transparent.rs rename {zcash_primitives/src/extensions => zcash_extensions/src}/transparent/demo.rs (82%) delete mode 100644 zcash_extensions_api/src/transparent.rs delete mode 100644 zcash_primitives/src/consensus/extensions.rs diff --git a/Cargo.toml b/Cargo.toml index 3ab4b6c3ef..9744357ebb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "components/equihash", "zcash_client_backend", "zcash_client_sqlite", - "zcash_extensions_api", + "zcash_extensions", "zcash_history", "zcash_primitives", "zcash_proofs", diff --git a/zcash_extensions_api/Cargo.toml b/zcash_extensions/Cargo.toml similarity index 53% rename from zcash_extensions_api/Cargo.toml rename to zcash_extensions/Cargo.toml index 2deb5dadf4..c975d48e96 100644 --- a/zcash_extensions_api/Cargo.toml +++ b/zcash_extensions/Cargo.toml @@ -1,11 +1,13 @@ [package] -name = "zcash_extensions_api" +name = "zcash_extensions" description = "TBD" version = "0.0.0" -authors = ["Jack Grigg "] +authors = ["Jack Grigg ", "Kris Nuttycombe "] homepage = "https://github.com/zcash/librustzcash" repository = "https://github.com/zcash/librustzcash" license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +blake2b_simd = "0.5" +zcash_primitives = { version = "0.3.0", path = "../zcash_primitives" } diff --git a/zcash_extensions_api/src/lib.rs b/zcash_extensions/src/consensus.rs similarity index 100% rename from zcash_extensions_api/src/lib.rs rename to zcash_extensions/src/consensus.rs diff --git a/zcash_primitives/src/consensus/extensions/transparent.rs b/zcash_extensions/src/consensus/transparent.rs similarity index 92% rename from zcash_primitives/src/consensus/extensions/transparent.rs rename to zcash_extensions/src/consensus/transparent.rs index 2c3e21745d..07769f368d 100644 --- a/zcash_primitives/src/consensus/extensions/transparent.rs +++ b/zcash_extensions/src/consensus/transparent.rs @@ -1,11 +1,11 @@ //! Consensus logic for Transparent Zcash Extensions. use std::convert::TryFrom; -use zcash_extensions_api::transparent::{Error, Extension, Precondition, Witness}; +use zcash_primitives::extensions::transparent::{Error, Extension, Precondition, Witness}; +use zcash_primitives::transaction::components::TzeOut; +use zcash_primitives::transaction::Transaction; -use crate::extensions::transparent::demo; -use crate::transaction::components::TzeOut; -use crate::transaction::Transaction; +use crate::transparent::demo; /// The set of programs that have assigned type IDs within the Zcash consensus rules. #[derive(Debug, Clone, Copy)] diff --git a/zcash_extensions/src/lib.rs b/zcash_extensions/src/lib.rs new file mode 100644 index 0000000000..3b6ce6424b --- /dev/null +++ b/zcash_extensions/src/lib.rs @@ -0,0 +1,2 @@ +pub mod consensus; +pub mod transparent; diff --git a/zcash_extensions/src/transparent.rs b/zcash_extensions/src/transparent.rs new file mode 100644 index 0000000000..48aca679f4 --- /dev/null +++ b/zcash_extensions/src/transparent.rs @@ -0,0 +1,3 @@ +//! Zcash transparent extensions. + +pub mod demo; diff --git a/zcash_primitives/src/extensions/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs similarity index 82% rename from zcash_primitives/src/extensions/transparent/demo.rs rename to zcash_extensions/src/transparent/demo.rs index cadd020dfd..95b64c7356 100644 --- a/zcash_primitives/src/extensions/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -21,9 +21,11 @@ use blake2b_simd::Params; use std::convert::TryFrom; use std::fmt; -use zcash_extensions_api::transparent::{Extension, FromPayload, ToPayload}; -use crate::transaction::components::TzeOut; +use zcash_primitives::extensions::transparent::{ + Extension, ExtensionTxBuilder, FromPayload, ToPayload, +}; +use zcash_primitives::transaction::components::{amount::Amount, OutPoint, TzeOut}; mod open { pub const MODE: usize = 0; @@ -273,6 +275,89 @@ impl Extension for Program { } } +fn builder_hashes(preimage_1: &[u8; 32], preimage_2: &[u8; 32]) -> ([u8; 32], [u8; 32]) { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(preimage_2).as_bytes()); + hash + }; + + let hash_1 = { + let mut hash = [0; 32]; + hash.copy_from_slice( + Params::new() + .hash_length(32) + .to_state() + .update(preimage_1) + .update(&hash_2) + .finalize() + .as_bytes(), + ); + hash + }; + + (hash_1, hash_2) +} + +pub struct DemoBuilder<'a, B> { + txn_builder: &'a mut B, + extension_id: usize, +} + +impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { + pub fn demo_open( + &mut self, + value: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32], + ) -> Result<(), B::BuildError> { + let (hash_1, _) = builder_hashes(&preimage_1, &preimage_2); + + // Call through to the generic builder. + self.txn_builder + .add_tze_output(self.extension_id, value, &Precondition::open(hash_1)) + } + + pub fn demo_transfer_to_close( + &mut self, + prevout: (OutPoint, TzeOut), + transfer_amount: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32], + ) -> Result<(), B::BuildError> { + let (_, hash_2) = builder_hashes(&preimage_1, &preimage_2); + + // should we eagerly validate the relationship between prevout.1 and preimage_1? + self.txn_builder + .add_tze_input(self.extension_id, prevout, move |_| { + Ok(Witness::open(preimage_1)) + })?; + + self.txn_builder.add_tze_output( + self.extension_id, + transfer_amount, // can this be > prevout.1.value? + &Precondition::close(hash_2), + ) + } + + pub fn demo_close( + &mut self, + prevout: (OutPoint, TzeOut), + preimage: [u8; 32], + ) -> Result<(), B::BuildError> { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage).as_bytes()); + hash + }; + + self.txn_builder + .add_tze_input(self.extension_id, prevout, move |_| { + Ok(Witness::close(preimage_2)) + }) + } +} + #[cfg(test)] mod tests { use blake2b_simd::Params; @@ -382,29 +467,49 @@ mod tests { hash }; - let mut mtx_a = TransactionData::nu4(); - mtx_a.tze_outputs.push(TzeOut { + // + // Opening transaction + // + + let out_a = TzeOut { value: Amount::from_u64(1).unwrap(), precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), - }); + }; + + println!("{:x?}", precondition.payload); + + let mut mtx_a = TransactionData::nu4(); + mtx_a.tze_outputs.push(out_a); let tx_a = mtx_a.freeze().unwrap(); - let mut mtx_b = TransactionData::nu4(); - mtx_b.tze_inputs.push(TzeIn { + // + // Transfer + // + + let in_b = TzeIn { prevout: OutPoint::new(tx_a.txid().0, 0), witness: tze::Witness::from(0, &Witness::open(preimage_1)), - }); - mtx_b.tze_outputs.push(TzeOut { + }; + let out_b = TzeOut { value: Amount::from_u64(1).unwrap(), precondition: tze::Precondition::from(0, &Precondition::close(hash_2)), - }); + }; + let mut mtx_b = TransactionData::nu4(); + mtx_b.tze_inputs.push(in_b); + mtx_b.tze_outputs.push(out_b); let tx_b = mtx_b.freeze().unwrap(); - let mut mtx_c = TransactionData::nu4(); - mtx_c.tze_inputs.push(TzeIn { + // + // Closing transaction + // + + let in_c = TzeIn { prevout: OutPoint::new(tx_b.txid().0, 0), witness: tze::Witness::from(0, &Witness::close(preimage_2)), - }); + }; + + let mut mtx_c = TransactionData::nu4(); + mtx_c.tze_inputs.push(in_c); let tx_c = mtx_c.freeze().unwrap(); // Verify tx_b diff --git a/zcash_extensions_api/src/transparent.rs b/zcash_extensions_api/src/transparent.rs deleted file mode 100644 index 028075f192..0000000000 --- a/zcash_extensions_api/src/transparent.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Core traits and structs for Transparent Zcash Extensions. - -use std::fmt; - -pub trait FromPayload: Sized { - /// Parses an extension type from a mode and payload. - fn from_payload(mode: usize, payload: &[u8]) -> Result; -} - -pub trait ToPayload { - /// Returns a serialized payload and its corresponding mode. - fn to_payload(&self) -> (usize, Vec); -} - -/// A condition that can be used to encumber transparent funds. -#[derive(Debug)] -pub struct Precondition { - pub extension_id: usize, - pub mode: usize, - pub payload: Vec, -} - -impl Precondition { - pub fn from(extension_id: usize, value: &P) -> Precondition { - let (mode, payload) = value.to_payload(); - Precondition { - extension_id, - mode, - payload, - } - } -} - -/// Data that satisfies the precondition for prior encumbered funds, enabling them to be -/// spent. -#[derive(Debug)] -pub struct Witness { - pub extension_id: usize, - pub mode: usize, - pub payload: Vec, -} - -impl Witness { - pub fn from(extension_id: usize, value: &P) -> Witness { - let (mode, payload) = value.to_payload(); - Witness { - extension_id, - mode, - payload, - } - } -} - -#[derive(Debug, PartialEq)] -pub enum Error { - InvalidForEpoch(u32, usize), - InvalidExtensionId(usize), - ProgramError(E), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidForEpoch(cid, ptype) => write!( - f, - "Program type {} is invalid for consensus branch id {}", - ptype, cid - ), - - Error::InvalidExtensionId(extension_id) => { - write!(f, "Unrecognized program type id {}", extension_id) - } - - Error::ProgramError(err) => write!(f, "Program error: {}", err), - } - } -} - -pub trait Extension { - type P; - type W; - type Error; - - fn verify_inner( - &self, - precondition: &Self::P, - witness: &Self::W, - context: &C, - ) -> Result<(), Self::Error>; - - fn verify( - &self, - precondition: &Precondition, - witness: &Witness, - context: &C, - ) -> Result<(), Self::Error> - where - Self::P: FromPayload, - Self::W: FromPayload, - { - self.verify_inner( - &Self::P::from_payload(precondition.mode, &precondition.payload)?, - &Self::W::from_payload(witness.mode, &witness.payload)?, - &context, - ) - } -} diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 0e91459a53..393ab55cdd 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -35,7 +35,6 @@ ripemd160 = { version = "0.9", optional = true } secp256k1 = { version = "0.17", optional = true } sha2 = "0.9" subtle = "2.2.1" -zcash_extensions_api = { version = "0.0", path = "../zcash_extensions_api" } [dev-dependencies] criterion = "0.3" diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 952c14aa38..cba33e4524 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -3,8 +3,6 @@ use std::convert::TryFrom; use std::fmt; -pub mod extensions; - /// Zcash consensus parameters. pub trait Parameters { fn activation_height(nu: NetworkUpgrade) -> Option; diff --git a/zcash_primitives/src/consensus/extensions.rs b/zcash_primitives/src/consensus/extensions.rs deleted file mode 100644 index 6210d3fcdd..0000000000 --- a/zcash_primitives/src/consensus/extensions.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Consensus logic for Zcash extensions. - -pub mod transparent; diff --git a/zcash_primitives/src/extensions.rs b/zcash_primitives/src/extensions.rs index 1aa5804f34..7d540a8bac 100644 --- a/zcash_primitives/src/extensions.rs +++ b/zcash_primitives/src/extensions.rs @@ -1,3 +1 @@ -//! Zcash extensions. - pub mod transparent; diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs index 48aca679f4..b2683518fe 100644 --- a/zcash_primitives/src/extensions/transparent.rs +++ b/zcash_primitives/src/extensions/transparent.rs @@ -1,3 +1,157 @@ -//! Zcash transparent extensions. +//! Core traits and structs for Transparent Zcash Extensions. -pub mod demo; +use crate::transaction::components::{Amount, OutPoint, TzeOut}; +use std::fmt; + +pub trait FromPayload: Sized { + /// Parses an extension type from a mode and payload. + fn from_payload(mode: usize, payload: &[u8]) -> Result; +} + +pub trait ToPayload { + /// Returns a serialized payload and its corresponding mode. + fn to_payload(&self) -> (usize, Vec); +} + +/// A condition that can be used to encumber transparent funds. +#[derive(Clone, Debug)] +pub struct Precondition { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Precondition { + pub fn from(extension_id: usize, value: &P) -> Precondition { + let (mode, payload) = value.to_payload(); + Precondition { + extension_id, + mode, + payload, + } + } +} + +/// Data that satisfies the precondition for prior encumbered funds, enabling them to be +/// spent. +#[derive(Clone, Debug)] +pub struct Witness { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Witness { + pub fn from(extension_id: usize, value: &P) -> Witness { + let (mode, payload) = value.to_payload(); + Witness { + extension_id, + mode, + payload, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidForEpoch(u32, usize), + InvalidExtensionId(usize), + ProgramError(E), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidForEpoch(cid, ptype) => write!( + f, + "Program type {} is invalid for consensus branch id {}", + ptype, cid + ), + + Error::InvalidExtensionId(extension_id) => { + write!(f, "Unrecognized program type id {}", extension_id) + } + + Error::ProgramError(err) => write!(f, "Program error: {}", err), + } + } +} + +pub trait Extension { + type P; + type W; + type Error; + + fn verify_inner( + &self, + precondition: &Self::P, + witness: &Self::W, + context: &C, + ) -> Result<(), Self::Error>; + + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Self::Error> + where + Self::P: FromPayload, + Self::W: FromPayload, + { + self.verify_inner( + &Self::P::from_payload(precondition.mode, &precondition.payload)?, + &Self::W::from_payload(witness.mode, &witness.payload)?, + &context, + ) + } +} + +// pub trait WitnessBuilder { +// type Error; +// type Witness: ToPayload; +// +// fn build_witness(ctx: BuildCtx) -> Result; +// } + +// This extension trait is satisfied by the transaction::builder::Builder type. It provides a +// minimal contract for interacting with the transaction builder, that extension library authors +// can use to add extension-specific builder traits that may be used to interact with the +// transaction builder. This may make it simpler for projects that include transaction-builder +// functionality to integrate with third-party extensions without those extensions being coupled to +// a particular transaction or builder representation. +pub trait ExtensionTxBuilder<'a> { + type BuildCtx; + type BuildError; + + fn add_tze_input( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder, + ) -> Result<(), Self::BuildError> + where + WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result); + //where WBuilder: WitnessBuilder; + + fn add_tze_output( + &mut self, + extension_id: usize, + value: Amount, + guarded_by: &P, + ) -> Result<(), Self::BuildError>; +} + +pub trait Epoch { + type VerifyError; + + // Implementation of this method should check that the provided witness + // satisfies the specified precondition, given the context. This verification + // becomes part of the consensus rules. + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &VerifyCtx, + ) -> Result<(), Error>; +} diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 302656edfd..c73825ceac 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -1,27 +1,33 @@ //! Structs for building transactions. -use crate::primitives::{Diversifier, Note, PaymentAddress}; -use crate::zip32::ExtendedSpendingKey; -use ff::Field; -use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; +use std::boxed::Box; use std::error; use std::fmt; use std::marker::PhantomData; +use ff::Field; +use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; + use crate::{ consensus, + extensions::transparent::{self as tze, ExtensionTxBuilder, ToPayload}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, note_encryption::{Memo, SaplingNoteEncryption}, + primitives::{Diversifier, Note, PaymentAddress}, prover::TxProver, redjubjub::PrivateKey, sapling::{spend_sig, Node}, transaction::{ - components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut}, + components::{ + amount::Amount, amount::DEFAULT_FEE, OutPoint, OutputDescription, SpendDescription, + TxOut, TzeIn, TzeOut, + }, signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, util::generate_random_rseed, + zip32::ExtendedSpendingKey, }; #[cfg(feature = "transparent-inputs")] @@ -185,17 +191,14 @@ struct TransparentInputs; impl TransparentInputs { #[cfg(feature = "transparent-inputs")] - fn push( - &mut self, - mtx: &mut TransactionData, - sk: secp256k1::SecretKey, - utxo: OutPoint, - coin: TxOut, - ) -> Result<(), Error> { + fn push(&mut self, sk: secp256k1::SecretKey, coin: TxOut) -> Result<(), Error> { if coin.value.is_negative() { return Err(Error::InvalidAmount); } + // ensure that the ripemd160 digest of the public key associated with the + // provided secret key matches that of the address to which the provided + // output may be spent let pubkey = secp256k1::PublicKey::from_secret_key(&self.secp, &sk).serialize(); match coin.script_pubkey.address() { Some(TransparentAddress::PublicKey(hash)) => { @@ -209,7 +212,6 @@ impl TransparentInputs { _ => return Err(Error::InvalidAddress), } - mtx.vin.push(TxIn::new(utxo)); self.inputs.push(TransparentInputInfo { sk, pubkey, coin }); Ok(()) @@ -243,6 +245,7 @@ impl TransparentInputs { consensus_branch_id, SIGHASH_ALL, Some((i, &info.coin.script_pubkey, info.coin.value)), + // tze equivalent is ??? )); let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); @@ -261,6 +264,42 @@ impl TransparentInputs { fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {} } +struct TzeInputInfo<'a, BuildCtx> { + prevout: TzeOut, + builder: Box Result + 'a>, +} + +struct TzeInputs<'a, BuildCtx> { + builders: Vec>, +} + +impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> { + fn push( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + builder: WBuilder, + ) where + WBuilder: 'a + FnOnce(&BuildCtx) -> Result, + { + let (outpoint, tzeout) = prevout; + self.builders.push(TzeInputInfo { + prevout: tzeout, + builder: Box::new(move |ctx| { + let (mode, payload) = builder(&ctx).map(|x| x.to_payload())?; + Ok(TzeIn { + prevout: outpoint, + witness: tze::Witness { + extension_id, + mode, + payload, + }, + }) + }), + }); + } +} + /// Metadata about a transaction created by a [`Builder`]. #[derive(Debug, PartialEq)] pub struct TransactionMetadata { @@ -300,7 +339,7 @@ impl TransactionMetadata { } /// Generates a [`Transaction`] from its inputs and outputs. -pub struct Builder { +pub struct Builder<'a, P: consensus::Parameters, R: RngCore + CryptoRng> { rng: R, height: u32, mtx: TransactionData, @@ -309,11 +348,12 @@ pub struct Builder { spends: Vec, outputs: Vec, transparent_inputs: TransparentInputs, + tze_inputs: TzeInputs<'a, TransactionData>, change_address: Option<(OutgoingViewingKey, PaymentAddress)>, phantom: PhantomData

, } -impl Builder { +impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> { /// Creates a new `Builder` targeted for inclusion in the block with the given height, /// using default values for general transaction fields and the default OS random. /// @@ -328,7 +368,7 @@ impl Builder { } } -impl Builder { +impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { /// Creates a new `Builder` targeted for inclusion in the block with the given height /// and randomness source, using default values for general transaction fields. /// @@ -338,7 +378,7 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: u32, rng: R) -> Builder { + pub fn new_with_rng(height: u32, rng: R) -> Builder<'a, P, R> { let mut mtx = TransactionData::new(); mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; @@ -351,6 +391,7 @@ impl Builder { spends: vec![], outputs: vec![], transparent_inputs: TransparentInputs::default(), + tze_inputs: TzeInputs { builders: vec![] }, change_address: None, phantom: PhantomData, } @@ -419,7 +460,8 @@ impl Builder { utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { - self.transparent_inputs.push(&mut self.mtx, sk, utxo, coin) + self.mtx.vin.push(TxIn::new(utxo)); + self.transparent_inputs.push(sk, coin) } /// Adds a transparent address to send funds to. @@ -461,6 +503,7 @@ impl Builder { mut self, consensus_branch_id: consensus::BranchId, prover: &impl TxProver, + // epoch: &Epoch ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); @@ -470,12 +513,20 @@ impl Builder { // Valid change let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() + - self.mtx.vout.iter().map(|vo| vo.value).sum::() + + self + .tze_inputs + .builders + .iter() + .map(|ein| ein.prevout.value) + .sum::() - self .mtx - .vout + .tze_outputs .iter() - .map(|output| output.value) + .map(|tzo| tzo.value) .sum::(); + if change.is_negative() { return Err(Error::ChangeIsNegative(change)); } @@ -657,7 +708,7 @@ impl Builder { } // - // Signatures + // Signatures -- all effects must have been applied. // let mut sighash = [0u8; 32]; @@ -689,6 +740,19 @@ impl Builder { self.mtx.binding_sig = None; } + // // Create TZE input witnesses + for tze_in in self.tze_inputs.builders { + // Need to enable witness to commit to the amount. + // - So hardware wallets "know" the amount without having to be sent all the + // prior TZE outputs to which this witness gives evidence. + // + // The witness is expected to commit to the precommitment internally? + // (Or make it part of the sighash?) + // - TODO: Check whether transparent inputs committing to script_pubkey was + // only so that hardware wallets "knew" what address was being spent from. + self.mtx.tze_inputs.push((tze_in.builder)(&self.mtx)?); + } + // Transparent signatures self.transparent_inputs .apply_signatures(&mut self.mtx, consensus_branch_id); @@ -700,6 +764,50 @@ impl Builder { } } +impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a> + for Builder<'a, P, R> +{ + type BuildCtx = TransactionData; + type BuildError = Error; + + fn add_tze_input( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder, + ) -> Result<(), Self::BuildError> + where + WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result), + { + // where WBuilder: WitnessBuilder { + self.tze_inputs.push(extension_id, prevout, witness_builder); + Ok(()) + } + + fn add_tze_output( + &mut self, + extension_id: usize, + value: Amount, + guarded_by: &G, + ) -> Result<(), Self::BuildError> { + if value.is_negative() { + return Err(Error::InvalidAmount); + } + + let (mode, payload) = guarded_by.to_payload(); + self.mtx.tze_outputs.push(TzeOut { + value, + precondition: tze::Precondition { + extension_id, + mode, + payload, + }, + }); + + Ok(()) + } +} + #[cfg(test)] mod tests { use ff::{Field, PrimeField}; diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index ad0219a8f1..277c78b3c8 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -4,8 +4,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::PrimeField; use group::GroupEncoding; use std::io::{self, Read, Write}; -use zcash_extensions_api::transparent as tze; +use crate::extensions::transparent as tze; use crate::legacy::Script; use crate::redjubjub::{PublicKey, Signature}; use crate::serialize::{CompactSize, Vector}; @@ -118,7 +118,7 @@ impl TxOut { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TzeIn { pub prevout: OutPoint, pub witness: tze::Witness, @@ -151,7 +151,7 @@ impl TzeIn { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TzeOut { pub value: Amount, pub precondition: tze::Precondition, From edfdc443f0c62121a18d60cdd026ff891785f269 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 18 May 2020 20:27:19 -0600 Subject: [PATCH 200/210] Make error a type member of FromPayload trait rather than a type variable. Fix demo test compilation errors. --- zcash_extensions/src/transparent/demo.rs | 33 +++++++++++-------- .../src/extensions/transparent.rs | 14 +++++--- zcash_primitives/src/transaction/builder.rs | 12 +++++-- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 95b64c7356..bff606ecf2 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -18,14 +18,15 @@ //! - `tx_b`: `[ TzeIn(tx_a, preimage_1) -> TzeOut(value, hash_2) ]` //! - `tx_c`: `[ TzeIn(tx_b, preimage_2) -> [any output types...] ]` -use blake2b_simd::Params; use std::convert::TryFrom; use std::fmt; -use zcash_primitives::extensions::transparent::{ - Extension, ExtensionTxBuilder, FromPayload, ToPayload, +use blake2b_simd::Params; + +use zcash_primitives::{ + extensions::transparent::{Extension, ExtensionTxBuilder, FromPayload, ToPayload}, + transaction::components::{amount::Amount, OutPoint, TzeOut}, }; -use zcash_primitives::transaction::components::{amount::Amount, OutPoint, TzeOut}; mod open { pub const MODE: usize = 0; @@ -100,8 +101,10 @@ impl TryFrom<(usize, Precondition)> for Precondition { } } -impl FromPayload for Precondition { - fn from_payload(mode: usize, payload: &[u8]) -> Result { +impl FromPayload for Precondition { + type Error = Error; + + fn from_payload(mode: usize, payload: &[u8]) -> Result { match mode { open::MODE => { if payload.len() == 32 { @@ -163,8 +166,10 @@ impl TryFrom<(usize, Witness)> for Witness { } } -impl FromPayload for Witness { - fn from_payload(mode: usize, payload: &[u8]) -> Result { +impl FromPayload for Witness { + type Error = Error; + + fn from_payload(mode: usize, payload: &[u8]) -> Result { match mode { open::MODE => { if payload.len() == 32 { @@ -361,12 +366,14 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { #[cfg(test)] mod tests { use blake2b_simd::Params; - use zcash_extensions_api::transparent::{self as tze, Extension, FromPayload, ToPayload}; use super::{close, open, Context, Precondition, Program, Witness}; - use crate::transaction::{ - components::{Amount, OutPoint, TzeIn, TzeOut}, - Transaction, TransactionData, + use zcash_primitives::{ + extensions::transparent::{self as tze, Extension, FromPayload, ToPayload}, + transaction::{ + components::{Amount, OutPoint, TzeIn, TzeOut}, + Transaction, TransactionData, + }, }; #[test] @@ -476,7 +483,7 @@ mod tests { precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), }; - println!("{:x?}", precondition.payload); + // println!("{:x?}", precondition.payload); let mut mtx_a = TransactionData::nu4(); mtx_a.tze_outputs.push(out_a); diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs index b2683518fe..ee4b75d305 100644 --- a/zcash_primitives/src/extensions/transparent.rs +++ b/zcash_primitives/src/extensions/transparent.rs @@ -3,9 +3,11 @@ use crate::transaction::components::{Amount, OutPoint, TzeOut}; use std::fmt; -pub trait FromPayload: Sized { +pub trait FromPayload: Sized { + type Error; + /// Parses an extension type from a mode and payload. - fn from_payload(mode: usize, payload: &[u8]) -> Result; + fn from_payload(mode: usize, payload: &[u8]) -> Result; } pub trait ToPayload { @@ -30,6 +32,10 @@ impl Precondition { payload, } } + + pub fn try_to(&self) -> Result { + P::from_payload(self.mode, &self.payload) + } } /// Data that satisfies the precondition for prior encumbered funds, enabling them to be @@ -96,8 +102,8 @@ pub trait Extension { context: &C, ) -> Result<(), Self::Error> where - Self::P: FromPayload, - Self::W: FromPayload, + Self::P: FromPayload, + Self::W: FromPayload, { self.verify_inner( &Self::P::from_payload(precondition.mode, &precondition.payload)?, diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index c73825ceac..337e5b6d66 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -274,6 +274,10 @@ struct TzeInputs<'a, BuildCtx> { } impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> { + fn default() -> Self { + TzeInputs { builders: vec![] } + } + fn push( &mut self, extension_id: usize, @@ -391,7 +395,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { spends: vec![], outputs: vec![], transparent_inputs: TransparentInputs::default(), - tze_inputs: TzeInputs { builders: vec![] }, + tze_inputs: TzeInputs::default(), change_address: None, phantom: PhantomData, } @@ -814,7 +818,6 @@ mod tests { use rand_core::OsRng; use std::marker::PhantomData; - use super::{Builder, Error}; use crate::{ consensus, consensus::TestNetwork, @@ -827,6 +830,8 @@ mod tests { zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; + use super::{Builder, Error, TzeInputs}; + #[test] fn fails_on_negative_output() { let extsk = ExtendedSpendingKey::master(&[]); @@ -853,7 +858,7 @@ mod tests { TestNetwork::activation_height(NetworkUpgrade::Sapling).unwrap(); // Create a builder with 0 fee, so we can construct t outputs - let mut builder = builder::Builder:: { + let mut builder = builder::Builder::<'_, TestNetwork, OsRng> { rng: OsRng, height: sapling_activation_height, mtx: TransactionData::new(), @@ -862,6 +867,7 @@ mod tests { spends: vec![], outputs: vec![], transparent_inputs: TransparentInputs::default(), + tze_inputs: TzeInputs::default(), change_address: None, phantom: PhantomData, }; From 225e05ea5e9fe9224f160ded19658842b09cb519 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 26 May 2020 14:49:02 -0600 Subject: [PATCH 201/210] Add validation to demo transaction builder. --- zcash_extensions/src/transparent/demo.rs | 54 +++++++++++++++---- .../src/extensions/transparent.rs | 8 --- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index bff606ecf2..62a26db756 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -309,18 +309,28 @@ pub struct DemoBuilder<'a, B> { extension_id: usize, } +pub enum DemoBuildError { + BaseBuilderError(E), + ExpectedOpen, + ExpectedClose, + PrevoutParseFailure(Error), + TransferMismatch { expected: [u8; 32], actual: [u8; 32] }, + CloseMismatch { expected: [u8; 32], actual: [u8; 32] }, +} + impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { pub fn demo_open( &mut self, value: Amount, preimage_1: [u8; 32], preimage_2: [u8; 32], - ) -> Result<(), B::BuildError> { + ) -> Result<(), DemoBuildError> { let (hash_1, _) = builder_hashes(&preimage_1, &preimage_2); // Call through to the generic builder. self.txn_builder .add_tze_output(self.extension_id, value, &Precondition::open(hash_1)) + .map_err(DemoBuildError::BaseBuilderError) } pub fn demo_transfer_to_close( @@ -329,37 +339,63 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { transfer_amount: Amount, preimage_1: [u8; 32], preimage_2: [u8; 32], - ) -> Result<(), B::BuildError> { - let (_, hash_2) = builder_hashes(&preimage_1, &preimage_2); + ) -> Result<(), DemoBuildError> { + let (hash_1, hash_2) = builder_hashes(&preimage_1, &preimage_2); + + // eagerly validate the relationship between prevout.1 and preimage_1 + match Precondition::from_payload(prevout.1.precondition.mode, &prevout.1.precondition.payload) { + Ok(Precondition::Open(hash)) => + if hash.0 != hash_1 { + Err(DemoBuildError::TransferMismatch { expected: hash.0, actual: hash_1})? + } + Ok(Precondition::Close(_)) => + Err(DemoBuildError::ExpectedOpen)?, + Err(parse_failure) => + Err(DemoBuildError::PrevoutParseFailure(parse_failure))? + } - // should we eagerly validate the relationship between prevout.1 and preimage_1? self.txn_builder .add_tze_input(self.extension_id, prevout, move |_| { Ok(Witness::open(preimage_1)) - })?; + }) + .map_err(DemoBuildError::BaseBuilderError)?; self.txn_builder.add_tze_output( self.extension_id, transfer_amount, // can this be > prevout.1.value? &Precondition::close(hash_2), ) + .map_err(DemoBuildError::BaseBuilderError) } pub fn demo_close( &mut self, prevout: (OutPoint, TzeOut), - preimage: [u8; 32], - ) -> Result<(), B::BuildError> { + preimage_2: [u8; 32], + ) -> Result<(), DemoBuildError> { let hash_2 = { let mut hash = [0; 32]; - hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage).as_bytes()); + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage_2).as_bytes()); hash }; + // eagerly validate the relationship between prevout.1 and preimage_2 + match Precondition::from_payload(prevout.1.precondition.mode, &prevout.1.precondition.payload) { + Ok(Precondition::Open(_)) => + Err(DemoBuildError::ExpectedClose)?, + Ok(Precondition::Close(hash)) => + if hash.0 != hash_2 { + Err(DemoBuildError::CloseMismatch { expected: hash.0, actual: hash_2})? + } + Err(parse_failure) => + Err(DemoBuildError::PrevoutParseFailure(parse_failure))? + } + self.txn_builder .add_tze_input(self.extension_id, prevout, move |_| { Ok(Witness::close(preimage_2)) }) + .map_err(DemoBuildError::BaseBuilderError) } } @@ -483,8 +519,6 @@ mod tests { precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), }; - // println!("{:x?}", precondition.payload); - let mut mtx_a = TransactionData::nu4(); mtx_a.tze_outputs.push(out_a); let tx_a = mtx_a.freeze().unwrap(); diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs index ee4b75d305..12ed3c5191 100644 --- a/zcash_primitives/src/extensions/transparent.rs +++ b/zcash_primitives/src/extensions/transparent.rs @@ -113,13 +113,6 @@ pub trait Extension { } } -// pub trait WitnessBuilder { -// type Error; -// type Witness: ToPayload; -// -// fn build_witness(ctx: BuildCtx) -> Result; -// } - // This extension trait is satisfied by the transaction::builder::Builder type. It provides a // minimal contract for interacting with the transaction builder, that extension library authors // can use to add extension-specific builder traits that may be used to interact with the @@ -138,7 +131,6 @@ pub trait ExtensionTxBuilder<'a> { ) -> Result<(), Self::BuildError> where WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result); - //where WBuilder: WitnessBuilder; fn add_tze_output( &mut self, From 1966a57ebf5b143c4560163ca4740c4d20fabb21 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 27 May 2020 12:09:59 -0600 Subject: [PATCH 202/210] Add tests for transaction builder support. --- zcash_extensions/Cargo.toml | 6 + zcash_extensions/src/transparent/demo.rs | 212 +++++++++++++++++--- zcash_primitives/src/prover.rs | 2 +- zcash_primitives/src/transaction/builder.rs | 53 +++-- zcash_primitives/src/transaction/mod.rs | 19 +- zcash_primitives/src/transaction/sighash.rs | 3 +- 6 files changed, 232 insertions(+), 63 deletions(-) diff --git a/zcash_extensions/Cargo.toml b/zcash_extensions/Cargo.toml index c975d48e96..454b1315b1 100644 --- a/zcash_extensions/Cargo.toml +++ b/zcash_extensions/Cargo.toml @@ -11,3 +11,9 @@ edition = "2018" [dependencies] blake2b_simd = "0.5" zcash_primitives = { version = "0.3.0", path = "../zcash_primitives" } + +[dev-dependencies] +ff = { version = "0.7", path = "../ff" } +jubjub = { version = "0.4", path = "../jubjub" } +zcash_proofs = { path = "../zcash_proofs" } +rand_core = "0.5.1" diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 62a26db756..68da1599d6 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -304,21 +304,28 @@ fn builder_hashes(preimage_1: &[u8; 32], preimage_2: &[u8; 32]) -> ([u8; 32], [u (hash_1, hash_2) } -pub struct DemoBuilder<'a, B> { - txn_builder: &'a mut B, - extension_id: usize, +pub struct DemoBuilder { + pub txn_builder: B, + pub extension_id: usize, } +#[derive(Debug)] pub enum DemoBuildError { BaseBuilderError(E), ExpectedOpen, ExpectedClose, PrevoutParseFailure(Error), - TransferMismatch { expected: [u8; 32], actual: [u8; 32] }, - CloseMismatch { expected: [u8; 32], actual: [u8; 32] }, + TransferMismatch { + expected: [u8; 32], + actual: [u8; 32], + }, + CloseMismatch { + expected: [u8; 32], + actual: [u8; 32], + }, } -impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { +impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> { pub fn demo_open( &mut self, value: Amount, @@ -343,15 +350,20 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { let (hash_1, hash_2) = builder_hashes(&preimage_1, &preimage_2); // eagerly validate the relationship between prevout.1 and preimage_1 - match Precondition::from_payload(prevout.1.precondition.mode, &prevout.1.precondition.payload) { - Ok(Precondition::Open(hash)) => + match Precondition::from_payload( + prevout.1.precondition.mode, + &prevout.1.precondition.payload, + ) { + Ok(Precondition::Open(hash)) => { if hash.0 != hash_1 { - Err(DemoBuildError::TransferMismatch { expected: hash.0, actual: hash_1})? - } - Ok(Precondition::Close(_)) => - Err(DemoBuildError::ExpectedOpen)?, - Err(parse_failure) => - Err(DemoBuildError::PrevoutParseFailure(parse_failure))? + Err(DemoBuildError::TransferMismatch { + expected: hash.0, + actual: hash_1, + })? + } + } + Ok(Precondition::Close(_)) => Err(DemoBuildError::ExpectedOpen)?, + Err(parse_failure) => Err(DemoBuildError::PrevoutParseFailure(parse_failure))?, } self.txn_builder @@ -360,12 +372,13 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { }) .map_err(DemoBuildError::BaseBuilderError)?; - self.txn_builder.add_tze_output( - self.extension_id, - transfer_amount, // can this be > prevout.1.value? - &Precondition::close(hash_2), - ) - .map_err(DemoBuildError::BaseBuilderError) + self.txn_builder + .add_tze_output( + self.extension_id, + transfer_amount, // can this be > prevout.1.value? + &Precondition::close(hash_2), + ) + .map_err(DemoBuildError::BaseBuilderError) } pub fn demo_close( @@ -380,15 +393,20 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { }; // eagerly validate the relationship between prevout.1 and preimage_2 - match Precondition::from_payload(prevout.1.precondition.mode, &prevout.1.precondition.payload) { - Ok(Precondition::Open(_)) => - Err(DemoBuildError::ExpectedClose)?, - Ok(Precondition::Close(hash)) => + match Precondition::from_payload( + prevout.1.precondition.mode, + &prevout.1.precondition.payload, + ) { + Ok(Precondition::Open(_)) => Err(DemoBuildError::ExpectedClose)?, + Ok(Precondition::Close(hash)) => { if hash.0 != hash_2 { - Err(DemoBuildError::CloseMismatch { expected: hash.0, actual: hash_2})? - } - Err(parse_failure) => - Err(DemoBuildError::PrevoutParseFailure(parse_failure))? + Err(DemoBuildError::CloseMismatch { + expected: hash.0, + actual: hash_2, + })? + } + } + Err(parse_failure) => Err(DemoBuildError::PrevoutParseFailure(parse_failure))?, } self.txn_builder @@ -401,17 +419,33 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { #[cfg(test)] mod tests { + use ff::{Field, PrimeField}; use blake2b_simd::Params; + use rand_core::OsRng; + + use zcash_proofs::prover::LocalTxProver; - use super::{close, open, Context, Precondition, Program, Witness}; use zcash_primitives::{ + consensus::{ + BranchId, + TestNetwork, + }, extensions::transparent::{self as tze, Extension, FromPayload, ToPayload}, + legacy::TransparentAddress, + merkle_tree::{CommitmentTree, IncrementalWitness}, + primitives::Rseed, + sapling::Node, transaction::{ + builder::Builder, components::{Amount, OutPoint, TzeIn, TzeOut}, Transaction, TransactionData, }, + zip32::ExtendedSpendingKey, }; + use super::{close, open, Context, DemoBuilder, Precondition, Program, Witness}; + + #[test] fn precondition_open_round_trip() { let data = vec![7; 32]; @@ -519,7 +553,7 @@ mod tests { precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), }; - let mut mtx_a = TransactionData::nu4(); + let mut mtx_a = TransactionData::future(); mtx_a.tze_outputs.push(out_a); let tx_a = mtx_a.freeze().unwrap(); @@ -535,7 +569,7 @@ mod tests { value: Amount::from_u64(1).unwrap(), precondition: tze::Precondition::from(0, &Precondition::close(hash_2)), }; - let mut mtx_b = TransactionData::nu4(); + let mut mtx_b = TransactionData::future(); mtx_b.tze_inputs.push(in_b); mtx_b.tze_outputs.push(out_b); let tx_b = mtx_b.freeze().unwrap(); @@ -549,7 +583,7 @@ mod tests { witness: tze::Witness::from(0, &Witness::close(preimage_2)), }; - let mut mtx_c = TransactionData::nu4(); + let mut mtx_c = TransactionData::future(); mtx_c.tze_inputs.push(in_c); let tx_c = mtx_c.freeze().unwrap(); @@ -579,4 +613,118 @@ mod tests { ); } } + + #[test] + fn demo_builder_program() { + let preimage_1 = [1; 32]; + let preimage_2 = [2; 32]; + + let prover = LocalTxProver::with_default_location().unwrap(); + + // + // Opening transaction + // + + let mut rng = OsRng; + let mut builder_a: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + + // create some inputs to spend + let extsk = ExtendedSpendingKey::master(&[]); + let to = extsk.default_address().unwrap().1; + let note1 = to + .create_note(110000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) + .unwrap(); + let cm1 = Node::new(note1.cmu().to_repr()); + let mut tree = CommitmentTree::new(); + // fake that the note appears in some previous + // shielded output + tree.append(cm1).unwrap(); + let witness1 = IncrementalWitness::from_tree(&tree); + + builder_a + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1.clone(), + witness1.path().unwrap(), + ) + .unwrap(); + + let mut db_a = DemoBuilder { + txn_builder: &mut builder_a, + extension_id: 0, + }; + + let value = Amount::from_u64(100000).unwrap(); + db_a.demo_open(value, preimage_1, preimage_2) + .map_err(|e| format!("open failure: {:?}", e)) + .unwrap(); + let (tx_a, _) = builder_a + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // + // Transfer + // + + let mut builder_b: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + let mut db_b = DemoBuilder { + txn_builder: &mut builder_b, + extension_id: 0, + }; + let prevout_a = (OutPoint::new(tx_a.txid().0, 0), tx_a.data.tze_outputs[0].clone()); + let value_xfr = Amount::from_u64(90000).unwrap(); + db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, preimage_2) + .map_err(|e| format!("transfer failure: {:?}", e)) + .unwrap(); + let (tx_b, _) = builder_b + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // + // Closing transaction + // + + let mut builder_c: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + let mut db_c = DemoBuilder { + txn_builder: &mut builder_c, + extension_id: 0, + }; + let prevout_b = (OutPoint::new(tx_a.txid().0, 0), tx_b.data.tze_outputs[0].clone()); + db_c.demo_close(prevout_b, preimage_2) + .map_err(|e| format!("close failure: {:?}", e)) + .unwrap(); + + builder_c.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::from_u64(80000).unwrap()) + .unwrap(); + + let (tx_c, _) = builder_c + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // Verify tx_b + let ctx0 = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_a.data.tze_outputs[0].precondition, + &tx_b.data.tze_inputs[0].witness, + &ctx0 + ), + Ok(()) + ); + + // Verify tx_c + let ctx1 = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_b.data.tze_outputs[0].precondition, + &tx_c.data.tze_inputs[0].witness, + &ctx1 + ), + Ok(()) + ); + } } diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 83cf4d4d12..9efc0cdb09 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -60,7 +60,7 @@ pub trait TxProver { } #[cfg(test)] -pub(crate) mod mock { +pub mod mock { use ff::Field; use rand_core::OsRng; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 337e5b6d66..aded75e311 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -370,20 +370,14 @@ impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> { pub fn new(height: u32) -> Self { Builder::new_with_rng(height, OsRng) } + + pub fn new_future(height: u32) -> Self { + Builder::new_with_rng_future(height, OsRng) + } } impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { - /// Creates a new `Builder` targeted for inclusion in the block with the given height - /// and randomness source, using default values for general transaction fields. - /// - /// # Default values - /// - /// The expiry height will be set to the given height plus the default transaction - /// expiry delta (20 blocks). - /// - /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: u32, rng: R) -> Builder<'a, P, R> { - let mut mtx = TransactionData::new(); + fn new_with_mtx(height: u32, rng: R, mut mtx: TransactionData) -> Builder<'a, P, R> { mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; Builder { @@ -401,6 +395,25 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { } } + /// Creates a new `Builder` targeted for inclusion in the block with the given height + /// and randomness source, using default values for general transaction fields. + /// + /// # Default values + /// + /// The expiry height will be set to the given height plus the default transaction + /// expiry delta (20 blocks). + /// + /// The fee will be set to the default fee (0.0001 ZEC). + pub fn new_with_rng(height: u32, rng: R) -> Builder<'a, P, R> { + let mtx = TransactionData::new(); + Self::new_with_mtx(height, rng, mtx) + } + + pub fn new_with_rng_future(height: u32, rng: R) -> Builder<'a, P, R> { + let mtx = TransactionData::future(); + Self::new_with_mtx(height, rng, mtx) + } + /// Adds a Sapling note to be spent in this transaction. /// /// Returns an error if the given Merkle path does not have the same anchor as the @@ -507,7 +520,6 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { mut self, consensus_branch_id: consensus::BranchId, prover: &impl TxProver, - // epoch: &Epoch ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); @@ -516,7 +528,9 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { // // Valid change - let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() + let change = self.mtx.value_balance + - self.fee + + self.transparent_inputs.value_sum() - self.mtx.vout.iter().map(|vo| vo.value).sum::() + self .tze_inputs @@ -734,15 +748,15 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { } // Add a binding signature if needed - if binding_sig_needed { - self.mtx.binding_sig = Some( + self.mtx.binding_sig = if binding_sig_needed { + Some( prover .binding_sig(&mut ctx, self.mtx.value_balance, &sighash) - .map_err(|()| Error::BindingSig)?, - ); + .map_err(|_| Error::BindingSig)?, + ) } else { - self.mtx.binding_sig = None; - } + None + }; // // Create TZE input witnesses for tze_in in self.tze_inputs.builders { @@ -783,7 +797,6 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a where WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result), { - // where WBuilder: WitnessBuilder { self.tze_inputs.push(extension_id, prevout, witness_builder); Ok(()) } diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 42cf8fc31a..2af83bfc9f 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -44,8 +44,8 @@ impl fmt::Display for TxId { /// A Zcash transaction. #[derive(Debug)] pub struct Transaction { - txid: TxId, - data: TransactionData, + pub txid: TxId, + pub data: TransactionData, } impl Deref for Transaction { @@ -142,7 +142,7 @@ impl TransactionData { } } - pub fn nu4() -> Self { + pub fn future() -> Self { TransactionData { overwintered: true, version: FUTURE_TX_VERSION, @@ -310,10 +310,11 @@ impl Transaction { let is_sapling_v4 = self.overwintered && self.version_group_id == SAPLING_VERSION_GROUP_ID && self.version == SAPLING_TX_VERSION; - let is_nu4_v5 = self.overwintered + let has_tze = self.overwintered && self.version_group_id == FUTURE_VERSION_GROUP_ID && self.version == FUTURE_TX_VERSION; - if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || is_nu4_v5) { + + if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Unknown transaction format", @@ -322,16 +323,16 @@ impl Transaction { Vector::write(&mut writer, &self.vin, |w, e| e.write(w))?; Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?; - if is_nu4_v5 { + if has_tze { Vector::write(&mut writer, &self.tze_inputs, |w, e| e.write(w))?; Vector::write(&mut writer, &self.tze_outputs, |w, e| e.write(w))?; } writer.write_u32::(self.lock_time)?; - if is_overwinter_v3 || is_sapling_v4 || is_nu4_v5 { + if is_overwinter_v3 || is_sapling_v4 || has_tze { writer.write_u32::(self.expiry_height)?; } - if is_sapling_v4 || is_nu4_v5 { + if is_sapling_v4 || has_tze { writer.write_all(&self.value_balance.to_i64_le_bytes())?; Vector::write(&mut writer, &self.shielded_spends, |w, e| e.write(w))?; Vector::write(&mut writer, &self.shielded_outputs, |w, e| e.write(w))?; @@ -376,7 +377,7 @@ impl Transaction { } } - if (is_sapling_v4 || is_nu4_v5) + if (is_sapling_v4 || has_tze) && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) { match self.binding_sig { diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 2cc847b59e..f8f0e010e1 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -6,7 +6,7 @@ use group::GroupEncoding; use super::{ components::{Amount, TxOut}, Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, - SAPLING_VERSION_GROUP_ID, + SAPLING_VERSION_GROUP_ID, FUTURE_VERSION_GROUP_ID, }; use crate::{consensus, legacy::Script}; @@ -54,6 +54,7 @@ impl SigHashVersion { match tx.version_group_id { OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, SAPLING_VERSION_GROUP_ID => SigHashVersion::Sapling, + FUTURE_VERSION_GROUP_ID => SigHashVersion::Sapling, //FIXME _ => unimplemented!(), } } else { From 019578837f5e1d02f73f26ee7eea63f03af18979 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 2 Jun 2020 16:20:44 -0600 Subject: [PATCH 203/210] Check transparent input for correctness before modifying vin. --- zcash_extensions/src/transparent/demo.rs | 26 +++++++++++++-------- zcash_primitives/src/transaction/builder.rs | 7 +++--- zcash_primitives/src/transaction/sighash.rs | 4 ++-- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 68da1599d6..db740a7e80 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -419,17 +419,14 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> { #[cfg(test)] mod tests { - use ff::{Field, PrimeField}; use blake2b_simd::Params; + use ff::{Field, PrimeField}; use rand_core::OsRng; use zcash_proofs::prover::LocalTxProver; use zcash_primitives::{ - consensus::{ - BranchId, - TestNetwork, - }, + consensus::{BranchId, TestNetwork}, extensions::transparent::{self as tze, Extension, FromPayload, ToPayload}, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, @@ -445,7 +442,6 @@ mod tests { use super::{close, open, Context, DemoBuilder, Precondition, Program, Witness}; - #[test] fn precondition_open_round_trip() { let data = vec![7; 32]; @@ -673,7 +669,10 @@ mod tests { txn_builder: &mut builder_b, extension_id: 0, }; - let prevout_a = (OutPoint::new(tx_a.txid().0, 0), tx_a.data.tze_outputs[0].clone()); + let prevout_a = ( + OutPoint::new(tx_a.txid().0, 0), + tx_a.data.tze_outputs[0].clone(), + ); let value_xfr = Amount::from_u64(90000).unwrap(); db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, preimage_2) .map_err(|e| format!("transfer failure: {:?}", e)) @@ -692,13 +691,20 @@ mod tests { txn_builder: &mut builder_c, extension_id: 0, }; - let prevout_b = (OutPoint::new(tx_a.txid().0, 0), tx_b.data.tze_outputs[0].clone()); + let prevout_b = ( + OutPoint::new(tx_a.txid().0, 0), + tx_b.data.tze_outputs[0].clone(), + ); db_c.demo_close(prevout_b, preimage_2) .map_err(|e| format!("close failure: {:?}", e)) .unwrap(); - builder_c.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::from_u64(80000).unwrap()) - .unwrap(); + builder_c + .add_transparent_output( + &TransparentAddress::PublicKey([0; 20]), + Amount::from_u64(80000).unwrap(), + ) + .unwrap(); let (tx_c, _) = builder_c .build(BranchId::Canopy, &prover) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index aded75e311..77680d2a89 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -477,8 +477,9 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { + self.transparent_inputs.push(sk, coin)?; self.mtx.vin.push(TxIn::new(utxo)); - self.transparent_inputs.push(sk, coin) + Ok(()); } /// Adds a transparent address to send funds to. @@ -528,9 +529,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { // // Valid change - let change = self.mtx.value_balance - - self.fee - + self.transparent_inputs.value_sum() + let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() - self.mtx.vout.iter().map(|vo| vo.value).sum::() + self .tze_inputs diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index f8f0e010e1..a59b235dc1 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -5,8 +5,8 @@ use group::GroupEncoding; use super::{ components::{Amount, TxOut}, - Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, - SAPLING_VERSION_GROUP_ID, FUTURE_VERSION_GROUP_ID, + Transaction, TransactionData, FUTURE_VERSION_GROUP_ID, OVERWINTER_VERSION_GROUP_ID, + SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, }; use crate::{consensus, legacy::Script}; From a3e49b6d063d85c452a192fb5f28cfcbcfc4f310 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Jun 2020 16:05:55 -0600 Subject: [PATCH 204/210] Keep transaction details private. --- zcash_extensions/src/transparent/demo.rs | 12 ++++++------ zcash_primitives/src/transaction/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index db740a7e80..def7f8450f 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -671,7 +671,7 @@ mod tests { }; let prevout_a = ( OutPoint::new(tx_a.txid().0, 0), - tx_a.data.tze_outputs[0].clone(), + tx_a.tze_outputs[0].clone(), ); let value_xfr = Amount::from_u64(90000).unwrap(); db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, preimage_2) @@ -693,7 +693,7 @@ mod tests { }; let prevout_b = ( OutPoint::new(tx_a.txid().0, 0), - tx_b.data.tze_outputs[0].clone(), + tx_b.tze_outputs[0].clone(), ); db_c.demo_close(prevout_b, preimage_2) .map_err(|e| format!("close failure: {:?}", e)) @@ -715,8 +715,8 @@ mod tests { let ctx0 = Ctx { tx: &tx_b }; assert_eq!( Program.verify( - &tx_a.data.tze_outputs[0].precondition, - &tx_b.data.tze_inputs[0].witness, + &tx_a.tze_outputs[0].precondition, + &tx_b.tze_inputs[0].witness, &ctx0 ), Ok(()) @@ -726,8 +726,8 @@ mod tests { let ctx1 = Ctx { tx: &tx_b }; assert_eq!( Program.verify( - &tx_b.data.tze_outputs[0].precondition, - &tx_c.data.tze_inputs[0].witness, + &tx_b.tze_outputs[0].precondition, + &tx_c.tze_inputs[0].witness, &ctx1 ), Ok(()) diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 2af83bfc9f..c22965e412 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -44,8 +44,8 @@ impl fmt::Display for TxId { /// A Zcash transaction. #[derive(Debug)] pub struct Transaction { - pub txid: TxId, - pub data: TransactionData, + txid: TxId, + data: TransactionData, } impl Deref for Transaction { From fd166115cd7712bc40863b89be4e3bcbb50ed738 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Jun 2020 20:38:05 -0600 Subject: [PATCH 205/210] Change extension_id and mode types to u32 --- zcash_extensions/src/consensus/transparent.rs | 10 +++--- zcash_extensions/src/transparent/demo.rs | 34 ++++++++---------- .../src/extensions/transparent.rs | 24 ++++++------- zcash_primitives/src/transaction/builder.rs | 6 ++-- .../src/transaction/components.rs | 35 ++++++++++++++----- 5 files changed, 61 insertions(+), 48 deletions(-) diff --git a/zcash_extensions/src/consensus/transparent.rs b/zcash_extensions/src/consensus/transparent.rs index 07769f368d..a4a3ed073b 100644 --- a/zcash_extensions/src/consensus/transparent.rs +++ b/zcash_extensions/src/consensus/transparent.rs @@ -13,12 +13,12 @@ pub enum ExtensionId { Demo, } -pub struct InvalidExtId(usize); +pub struct InvalidExtId(u32); -impl TryFrom for ExtensionId { +impl TryFrom for ExtensionId { type Error = InvalidExtId; - fn try_from(t: usize) -> Result { + fn try_from(t: u32) -> Result { match t { 0 => Ok(ExtensionId::Demo), n => Err(InvalidExtId(n)), @@ -26,8 +26,8 @@ impl TryFrom for ExtensionId { } } -impl From for usize { - fn from(type_id: ExtensionId) -> usize { +impl From for u32 { + fn from(type_id: ExtensionId) -> u32 { match type_id { ExtensionId::Demo => 0, } diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index def7f8450f..f2a4790d60 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -29,7 +29,7 @@ use zcash_primitives::{ }; mod open { - pub const MODE: usize = 0; + pub const MODE: u32 = 0; #[derive(Debug, PartialEq)] pub struct Precondition(pub [u8; 32]); @@ -39,7 +39,7 @@ mod open { } mod close { - pub const MODE: usize = 1; + pub const MODE: u32 = 1; #[derive(Debug, PartialEq)] pub struct Precondition(pub [u8; 32]); @@ -67,7 +67,7 @@ impl Precondition { #[derive(Debug, PartialEq)] pub enum Error { IllegalPayloadLength(usize), - ModeInvalid(usize), + ModeInvalid(u32), NonTzeTxn, HashMismatch, // include hashes? ModeMismatch, @@ -89,10 +89,10 @@ impl fmt::Display for Error { } } -impl TryFrom<(usize, Precondition)> for Precondition { +impl TryFrom<(u32, Precondition)> for Precondition { type Error = Error; - fn try_from(from: (usize, Self)) -> Result { + fn try_from(from: (u32, Self)) -> Result { match from { (open::MODE, Precondition::Open(p)) => Ok(Precondition::Open(p)), (close::MODE, Precondition::Close(p)) => Ok(Precondition::Close(p)), @@ -104,7 +104,7 @@ impl TryFrom<(usize, Precondition)> for Precondition { impl FromPayload for Precondition { type Error = Error; - fn from_payload(mode: usize, payload: &[u8]) -> Result { + fn from_payload(mode: u32, payload: &[u8]) -> Result { match mode { open::MODE => { if payload.len() == 32 { @@ -130,7 +130,7 @@ impl FromPayload for Precondition { } impl ToPayload for Precondition { - fn to_payload(&self) -> (usize, Vec) { + fn to_payload(&self) -> (u32, Vec) { match self { Precondition::Open(p) => (open::MODE, p.0.to_vec()), Precondition::Close(p) => (close::MODE, p.0.to_vec()), @@ -154,10 +154,10 @@ impl Witness { } } -impl TryFrom<(usize, Witness)> for Witness { +impl TryFrom<(u32, Witness)> for Witness { type Error = Error; - fn try_from(from: (usize, Self)) -> Result { + fn try_from(from: (u32, Self)) -> Result { match from { (open::MODE, Witness::Open(p)) => Ok(Witness::Open(p)), (close::MODE, Witness::Close(p)) => Ok(Witness::Close(p)), @@ -169,7 +169,7 @@ impl TryFrom<(usize, Witness)> for Witness { impl FromPayload for Witness { type Error = Error; - fn from_payload(mode: usize, payload: &[u8]) -> Result { + fn from_payload(mode: u32, payload: &[u8]) -> Result { match mode { open::MODE => { if payload.len() == 32 { @@ -195,7 +195,7 @@ impl FromPayload for Witness { } impl ToPayload for Witness { - fn to_payload(&self) -> (usize, Vec) { + fn to_payload(&self) -> (u32, Vec) { match self { Witness::Open(w) => (open::MODE, w.0.to_vec()), Witness::Close(w) => (close::MODE, w.0.to_vec()), @@ -306,7 +306,7 @@ fn builder_hashes(preimage_1: &[u8; 32], preimage_2: &[u8; 32]) -> ([u8; 32], [u pub struct DemoBuilder { pub txn_builder: B, - pub extension_id: usize, + pub extension_id: u32, } #[derive(Debug)] @@ -669,10 +669,7 @@ mod tests { txn_builder: &mut builder_b, extension_id: 0, }; - let prevout_a = ( - OutPoint::new(tx_a.txid().0, 0), - tx_a.tze_outputs[0].clone(), - ); + let prevout_a = (OutPoint::new(tx_a.txid().0, 0), tx_a.tze_outputs[0].clone()); let value_xfr = Amount::from_u64(90000).unwrap(); db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, preimage_2) .map_err(|e| format!("transfer failure: {:?}", e)) @@ -691,10 +688,7 @@ mod tests { txn_builder: &mut builder_c, extension_id: 0, }; - let prevout_b = ( - OutPoint::new(tx_a.txid().0, 0), - tx_b.tze_outputs[0].clone(), - ); + let prevout_b = (OutPoint::new(tx_a.txid().0, 0), tx_b.tze_outputs[0].clone()); db_c.demo_close(prevout_b, preimage_2) .map_err(|e| format!("close failure: {:?}", e)) .unwrap(); diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs index 12ed3c5191..577fad011a 100644 --- a/zcash_primitives/src/extensions/transparent.rs +++ b/zcash_primitives/src/extensions/transparent.rs @@ -7,24 +7,24 @@ pub trait FromPayload: Sized { type Error; /// Parses an extension type from a mode and payload. - fn from_payload(mode: usize, payload: &[u8]) -> Result; + fn from_payload(mode: u32, payload: &[u8]) -> Result; } pub trait ToPayload { /// Returns a serialized payload and its corresponding mode. - fn to_payload(&self) -> (usize, Vec); + fn to_payload(&self) -> (u32, Vec); } /// A condition that can be used to encumber transparent funds. #[derive(Clone, Debug)] pub struct Precondition { - pub extension_id: usize, - pub mode: usize, + pub extension_id: u32, + pub mode: u32, pub payload: Vec, } impl Precondition { - pub fn from(extension_id: usize, value: &P) -> Precondition { + pub fn from(extension_id: u32, value: &P) -> Precondition { let (mode, payload) = value.to_payload(); Precondition { extension_id, @@ -42,13 +42,13 @@ impl Precondition { /// spent. #[derive(Clone, Debug)] pub struct Witness { - pub extension_id: usize, - pub mode: usize, + pub extension_id: u32, + pub mode: u32, pub payload: Vec, } impl Witness { - pub fn from(extension_id: usize, value: &P) -> Witness { + pub fn from(extension_id: u32, value: &P) -> Witness { let (mode, payload) = value.to_payload(); Witness { extension_id, @@ -60,8 +60,8 @@ impl Witness { #[derive(Debug, PartialEq)] pub enum Error { - InvalidForEpoch(u32, usize), - InvalidExtensionId(usize), + InvalidForEpoch(u32, u32), + InvalidExtensionId(u32), ProgramError(E), } @@ -125,7 +125,7 @@ pub trait ExtensionTxBuilder<'a> { fn add_tze_input( &mut self, - extension_id: usize, + extension_id: u32, prevout: (OutPoint, TzeOut), witness_builder: WBuilder, ) -> Result<(), Self::BuildError> @@ -134,7 +134,7 @@ pub trait ExtensionTxBuilder<'a> { fn add_tze_output( &mut self, - extension_id: usize, + extension_id: u32, value: Amount, guarded_by: &P, ) -> Result<(), Self::BuildError>; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 77680d2a89..f743493ba4 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -280,7 +280,7 @@ impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> { fn push( &mut self, - extension_id: usize, + extension_id: u32, prevout: (OutPoint, TzeOut), builder: WBuilder, ) where @@ -789,7 +789,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a fn add_tze_input( &mut self, - extension_id: usize, + extension_id: u32, prevout: (OutPoint, TzeOut), witness_builder: WBuilder, ) -> Result<(), Self::BuildError> @@ -802,7 +802,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a fn add_tze_output( &mut self, - extension_id: usize, + extension_id: u32, value: Amount, guarded_by: &G, ) -> Result<(), Self::BuildError> { diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 277c78b3c8..39934baac9 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -1,8 +1,11 @@ //! Structs representing the components within Zcash transactions. use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + use ff::PrimeField; use group::GroupEncoding; + +use std::convert::TryFrom; use std::io::{self, Read, Write}; use crate::extensions::transparent as tze; @@ -124,6 +127,10 @@ pub struct TzeIn { pub witness: tze::Witness, } +fn to_io_error(_: std::num::TryFromIntError) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, "value out of range") +} + impl TzeIn { pub fn read(mut reader: &mut R) -> io::Result { let prevout = OutPoint::read(&mut reader)?; @@ -135,8 +142,8 @@ impl TzeIn { Ok(TzeIn { prevout, witness: tze::Witness { - extension_id, - mode, + extension_id: u32::try_from(extension_id).map_err(|e| to_io_error(e))?, + mode: u32::try_from(mode).map_err(|e| to_io_error(e))?, payload, }, }) @@ -145,8 +152,14 @@ impl TzeIn { pub fn write(&self, mut writer: W) -> io::Result<()> { self.prevout.write(&mut writer)?; - CompactSize::write(&mut writer, self.witness.extension_id)?; - CompactSize::write(&mut writer, self.witness.mode)?; + CompactSize::write( + &mut writer, + usize::try_from(self.witness.extension_id).map_err(|e| to_io_error(e))?, + )?; + CompactSize::write( + &mut writer, + usize::try_from(self.witness.mode).map_err(|e| to_io_error(e))?, + )?; Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b)) } } @@ -173,8 +186,8 @@ impl TzeOut { Ok(TzeOut { value, precondition: tze::Precondition { - extension_id, - mode, + extension_id: u32::try_from(extension_id).map_err(|e| to_io_error(e))?, + mode: u32::try_from(mode).map_err(|e| to_io_error(e))?, payload, }, }) @@ -183,8 +196,14 @@ impl TzeOut { pub fn write(&self, mut writer: W) -> io::Result<()> { writer.write_all(&self.value.to_i64_le_bytes())?; - CompactSize::write(&mut writer, self.precondition.extension_id)?; - CompactSize::write(&mut writer, self.precondition.mode)?; + CompactSize::write( + &mut writer, + usize::try_from(self.precondition.extension_id).map_err(|e| to_io_error(e))?, + )?; + CompactSize::write( + &mut writer, + usize::try_from(self.precondition.mode).map_err(|e| to_io_error(e))?, + )?; Vector::write(&mut writer, &self.precondition.payload, |w, b| { w.write_u8(*b) }) From a2d8b30baccbdcb911a1281643b64e87db56a1e6 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 3 Jun 2020 20:39:43 -0600 Subject: [PATCH 206/210] Add signature hash over TZE data. --- zcash_primitives/src/transaction/builder.rs | 7 +- zcash_primitives/src/transaction/mod.rs | 2 +- zcash_primitives/src/transaction/sighash.rs | 100 +++++++++++++++----- zcash_primitives/src/transaction/tests.rs | 29 +++--- 4 files changed, 100 insertions(+), 38 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index f743493ba4..a9ec48247f 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -24,7 +24,7 @@ use crate::{ amount::Amount, amount::DEFAULT_FEE, OutPoint, OutputDescription, SpendDescription, TxOut, TzeIn, TzeOut, }, - signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, + signature_hash_data, SignableInput, Transaction, TransactionData, SIGHASH_ALL, }, util::generate_random_rseed, zip32::ExtendedSpendingKey, @@ -244,8 +244,7 @@ impl TransparentInputs { mtx, consensus_branch_id, SIGHASH_ALL, - Some((i, &info.coin.script_pubkey, info.coin.value)), - // tze equivalent is ??? + SignableInput::transparent(i, &info.coin.script_pubkey, info.coin.value), )); let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); @@ -733,7 +732,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { &self.mtx, consensus_branch_id, SIGHASH_ALL, - None, + SignableInput::Shielded, )); // Create Sapling spendAuth and binding signatures diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index c22965e412..7c4d33a5fa 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -17,7 +17,7 @@ mod sighash; #[cfg(test)] mod tests; -pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL}; +pub use self::sighash::{signature_hash, signature_hash_data, SignableInput, SIGHASH_ALL}; use self::components::{ Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut, diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index a59b235dc1..e4e6bb4645 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -8,7 +8,7 @@ use super::{ Transaction, TransactionData, FUTURE_VERSION_GROUP_ID, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, }; -use crate::{consensus, legacy::Script}; +use crate::{consensus, extensions::transparent::Precondition, legacy::Script}; const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash"; const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash"; @@ -17,6 +17,7 @@ const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash"; const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash"; const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash"; const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash"; +const ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR: &[u8; 16] = b"ZcashTZE_SigHash"; pub const SIGHASH_ALL: u32 = 1; const SIGHASH_NONE: u32 = 2; @@ -152,11 +153,43 @@ fn shielded_outputs_hash(tx: &TransactionData) -> Blake2bHash { .hash(&data) } -pub fn signature_hash_data( +pub enum SignableInput<'a> { + Shielded, + Transparent { + index: usize, + script_code: &'a Script, + value: Amount, + }, + Tze { + index: usize, + precondition: &'a Precondition, + value: Amount, + }, +} + +impl<'a> SignableInput<'a> { + pub fn transparent(index: usize, script_code: &'a Script, value: Amount) -> Self { + SignableInput::Transparent { + index, + script_code, + value, + } + } + + pub fn tze(index: usize, precondition: &'a Precondition, value: Amount) -> Self { + SignableInput::Tze { + index, + precondition, + value, + } + } +} + +pub fn signature_hash_data<'a>( tx: &TransactionData, consensus_branch_id: consensus::BranchId, hash_type: u32, - transparent_input: Option<(usize, &Script, Amount)>, + signable_input: SignableInput<'a>, ) -> Vec { let sigversion = SigHashVersion::from_tx(tx); match sigversion { @@ -183,17 +216,18 @@ pub fn signature_hash_data( && (hash_type & SIGHASH_MASK) != SIGHASH_NONE, sequence_hash(tx) ); + if (hash_type & SIGHASH_MASK) != SIGHASH_SINGLE && (hash_type & SIGHASH_MASK) != SIGHASH_NONE { h.update(outputs_hash(tx).as_ref()); - } else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE - && transparent_input.is_some() - && transparent_input.as_ref().unwrap().0 < tx.vout.len() - { - h.update( - single_output_hash(&tx.vout[transparent_input.as_ref().unwrap().0]).as_ref(), - ); + } else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE { + match signable_input { + SignableInput::Transparent { index, .. } if index < tx.vout.len() => { + h.update(single_output_hash(&tx.vout[index]).as_ref()) + } + _ => h.update(&[0; 32]), + }; } else { h.update(&[0; 32]); }; @@ -213,15 +247,37 @@ pub fn signature_hash_data( } update_u32!(h, hash_type, tmp); - if let Some((n, script_code, amount)) = transparent_input { - let mut data = vec![]; - tx.vin[n].prevout.write(&mut data).unwrap(); - script_code.write(&mut data).unwrap(); - data.extend_from_slice(&amount.to_i64_le_bytes()); - (&mut data) - .write_u32::(tx.vin[n].sequence) - .unwrap(); - h.update(&data); + match signable_input { + SignableInput::Transparent { + index, + script_code, + value, + } => { + let mut data = vec![]; + tx.vin[index].prevout.write(&mut data).unwrap(); + script_code.write(&mut data).unwrap(); + data.extend_from_slice(&value.to_i64_le_bytes()); + (&mut data) + .write_u32::(tx.vin[index].sequence) + .unwrap(); + h.update(&data); + } + SignableInput::Tze { + index, + precondition, + value, + } => { + let mut data = ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR.to_vec(); + + tx.tze_inputs[index].prevout.write(&mut data).unwrap(); + data.write_u32::(precondition.extension_id) + .unwrap(); + data.write_u32::(precondition.mode).unwrap(); + data.extend(&precondition.payload); + data.extend_from_slice(&value.to_i64_le_bytes()); + h.update(&data); + } + _ => (), } h.finalize().as_ref().to_vec() @@ -230,11 +286,11 @@ pub fn signature_hash_data( } } -pub fn signature_hash( +pub fn signature_hash<'a>( tx: &Transaction, consensus_branch_id: consensus::BranchId, hash_type: u32, - transparent_input: Option<(usize, &Script, Amount)>, + signable_input: SignableInput<'a>, ) -> Vec { - signature_hash_data(tx, consensus_branch_id, hash_type, transparent_input) + signature_hash_data(tx, consensus_branch_id, hash_type, signable_input) } diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index a9c89776d8..8f6c90d035 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -1,9 +1,14 @@ use ff::Field; use rand_core::OsRng; -use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData}; use crate::{constants::SPENDING_KEY_GENERATOR, redjubjub::PrivateKey}; +use super::{ + components::Amount, + sighash::{signature_hash, SignableInput}, + Transaction, TransactionData, +}; + #[test] fn tx_read_write() { let data = &self::data::tx_read_write::TX_READ_WRITE; @@ -66,16 +71,17 @@ mod data; fn zip_0143() { for tv in self::data::zip_0143::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = tv.transparent_input.map(|n| { - ( + let signable_input = match tv.transparent_input { + Some(n) => SignableInput::transparent( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - ) - }); + ), + _ => SignableInput::Shielded, + }; assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, signable_input), tv.sighash ); } @@ -85,16 +91,17 @@ fn zip_0143() { fn zip_0243() { for tv in self::data::zip_0243::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = tv.transparent_input.map(|n| { - ( + let signable_input = match tv.transparent_input { + Some(n) => SignableInput::transparent( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - ) - }); + ), + _ => SignableInput::Shielded, + }; assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, signable_input), tv.sighash ); } From aee59f23a9ac243ad205b8e74269ca1d88f9592c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 5 Jun 2020 00:30:04 +1200 Subject: [PATCH 207/210] zcash_extensions: Only run demo builder test if we have parameters --- zcash_extensions/src/transparent/demo.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index f2a4790d60..bab8f975f8 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -615,7 +615,11 @@ mod tests { let preimage_1 = [1; 32]; let preimage_2 = [2; 32]; - let prover = LocalTxProver::with_default_location().unwrap(); + // Only run the test if we have the prover parameters. + let prover = match LocalTxProver::with_default_location() { + Some(prover) => prover, + None => return, + }; // // Opening transaction From eaf4f28c93e7c25dd4505ba1eecfab630fa589ec Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 5 Jun 2020 17:20:28 -0600 Subject: [PATCH 208/210] Add test for tze txn parsing. --- Cargo.toml | 4 +++ zcash_extensions/src/consensus/transparent.rs | 5 ++-- zcash_primitives/src/transaction/mod.rs | 1 + zcash_primitives/src/transaction/sighash.rs | 8 +++-- zcash_primitives/src/transaction/tests.rs | 30 +++++++++++++++++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9744357ebb..d321054995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,7 @@ members = [ lto = true panic = 'abort' codegen-units = 1 + +[profile.dev] +debug = true +opt-level = 0 diff --git a/zcash_extensions/src/consensus/transparent.rs b/zcash_extensions/src/consensus/transparent.rs index a4a3ed073b..de06781ff3 100644 --- a/zcash_extensions/src/consensus/transparent.rs +++ b/zcash_extensions/src/consensus/transparent.rs @@ -75,7 +75,7 @@ impl<'a> demo::Context for Context<'a> { } /// Wire identifier for the dummy network upgrade epoch. -pub const V1_EPOCH_ID: u32 = 0x7473_6554; +pub const NEXT_BRANCH_ID: u32 = 0x7374f403; /// A set of demo TZEs associated with the dummy network upgrade. struct EpochV1; @@ -102,8 +102,9 @@ impl Epoch for EpochV1 { pub fn epoch_for_branch(consensus_branch_id: u32) -> Option>> { // Map from consensus branch IDs to epochs. + let _tmp_branch_id = NEXT_BRANCH_ID; match consensus_branch_id { - V1_EPOCH_ID => Some(Box::new(EpochV1)), + NEXT_BRANCH_ID => Some(Box::new(EpochV1)), _ => None, } } diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 7c4d33a5fa..90399ace1f 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -231,6 +231,7 @@ impl Transaction { } else { (vec![], vec![]) }; + let lock_time = reader.read_u32::()?; let expiry_height = if is_overwinter_v3 || is_sapling_v4 || has_tze { reader.read_u32::()? diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index e4e6bb4645..3845684934 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -47,6 +47,7 @@ enum SigHashVersion { Sprout, Overwinter, Sapling, + Next, } impl SigHashVersion { @@ -55,7 +56,7 @@ impl SigHashVersion { match tx.version_group_id { OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, SAPLING_VERSION_GROUP_ID => SigHashVersion::Sapling, - FUTURE_VERSION_GROUP_ID => SigHashVersion::Sapling, //FIXME + FUTURE_VERSION_GROUP_ID => SigHashVersion::Next, _ => unimplemented!(), } } else { @@ -193,7 +194,7 @@ pub fn signature_hash_data<'a>( ) -> Vec { let sigversion = SigHashVersion::from_tx(tx); match sigversion { - SigHashVersion::Overwinter | SigHashVersion::Sapling => { + SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::Next => { let mut personal = [0; 16]; (&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX); (&mut personal[12..]) @@ -262,11 +263,12 @@ pub fn signature_hash_data<'a>( .unwrap(); h.update(&data); } + SignableInput::Tze { index, precondition, value, - } => { + } if sigversion == SigHashVersion::Next => { let mut data = ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR.to_vec(); tx.tze_inputs[index].prevout.write(&mut data).unwrap(); diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index 8f6c90d035..1e7a4427c2 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -66,6 +66,36 @@ fn tx_write_rejects_unexpected_binding_sig() { } } +#[test] +fn test_tze_tx_parse() { + let txn_bytes = vec![ + 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, 0xd9, 0x81, 0x80, 0x87, 0xde, 0x72, 0x44, 0xab, 0xc1, 0xb5, 0xfc, + 0xf2, 0x8e, 0x55, 0xe4, 0x2c, 0x7f, 0xf9, 0xc6, 0x78, 0xc0, 0x60, 0x51, 0x81, 0xf3, 0x7a, + 0xc5, 0xd7, 0x41, 0x4a, 0x7b, 0x95, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let tx = Transaction::read(&txn_bytes[..]); + + match tx { + Ok(tx) => assert!(!tx.tze_inputs.is_empty()), + + Err(e) => assert!( + false, + format!( + "An error occurred parsing a serialized TZE transaction: {}", + e + ) + ), + } +} + mod data; #[test] fn zip_0143() { From 0396ddaad3e4fa4c6a3227ae159f78225bff13a7 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 23 Jun 2020 10:27:36 -0600 Subject: [PATCH 209/210] Update to non-yanked version of subtle crate. --- zcash_client_backend/Cargo.toml | 2 +- zcash_extensions/Cargo.toml | 6 +++--- zcash_primitives/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index a4ce4290ec..f2ffcbc367 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -20,7 +20,7 @@ group = "0.7" hex = "0.4" jubjub = "0.4" protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 -subtle = "2" +subtle = "2.2.3" zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [build-dependencies] diff --git a/zcash_extensions/Cargo.toml b/zcash_extensions/Cargo.toml index 454b1315b1..acdd782e06 100644 --- a/zcash_extensions/Cargo.toml +++ b/zcash_extensions/Cargo.toml @@ -13,7 +13,7 @@ blake2b_simd = "0.5" zcash_primitives = { version = "0.3.0", path = "../zcash_primitives" } [dev-dependencies] -ff = { version = "0.7", path = "../ff" } -jubjub = { version = "0.4", path = "../jubjub" } -zcash_proofs = { path = "../zcash_proofs" } +ff = "0.7" +jubjub = "0.4" rand_core = "0.5.1" +zcash_proofs = { path = "../zcash_proofs" } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 393ab55cdd..0c99a59d92 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -34,7 +34,7 @@ rand_core = "0.5.1" ripemd160 = { version = "0.9", optional = true } secp256k1 = { version = "0.17", optional = true } sha2 = "0.9" -subtle = "2.2.1" +subtle = "2.2.3" [dev-dependencies] criterion = "0.3" From 78bb15fa4b344ebda36c4cacb29e53eeb8c53eef Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 26 Jun 2020 09:57:02 -0600 Subject: [PATCH 210/210] Formatter fix. --- zcash_primitives/src/transaction/sighash.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 3845684934..7c7691ff95 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -47,7 +47,7 @@ enum SigHashVersion { Sprout, Overwinter, Sapling, - Next, + Future, } impl SigHashVersion { @@ -56,7 +56,7 @@ impl SigHashVersion { match tx.version_group_id { OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, SAPLING_VERSION_GROUP_ID => SigHashVersion::Sapling, - FUTURE_VERSION_GROUP_ID => SigHashVersion::Next, + FUTURE_VERSION_GROUP_ID => SigHashVersion::Future, _ => unimplemented!(), } } else { @@ -194,7 +194,7 @@ pub fn signature_hash_data<'a>( ) -> Vec { let sigversion = SigHashVersion::from_tx(tx); match sigversion { - SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::Next => { + SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::Future => { let mut personal = [0; 16]; (&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX); (&mut personal[12..]) @@ -268,7 +268,7 @@ pub fn signature_hash_data<'a>( index, precondition, value, - } if sigversion == SigHashVersion::Next => { + } if sigversion == SigHashVersion::Future => { let mut data = ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR.to_vec(); tx.tze_inputs[index].prevout.write(&mut data).unwrap();