* Partial move to ff 0.13

It turns out the newly released k256 0.12 isn't on ff 0.13, preventing further
work at this time.

* Update all crates to work on ff 0.13

The provided curves still need to be expanded to fit the new API.

* Finish adding dalek-ff-group ff 0.13 constants

* Correct FieldElement::product definition

Also stops exporting macros.

* Test most new parts of ff 0.13

* Additionally test ff-group-tests with BLS12-381 and the pasta curves

We only tested curves from RustCrypto. Now we test a curve offered by zk-crypto,
the group behind ff/group, and the pasta curves, which is by Zcash (though
Zcash developers are also behind zk-crypto).

* Finish Ed448

Fully specifies all constants, passes all tests in ff-group-tests, and finishes moving to ff-0.13.

* Add RustCrypto/elliptic-curves to allowed git repos

Needed due to k256/p256 incorrectly defining product.

* Finish writing ff 0.13 tests

* Add additional comments to dalek

* Further comments

* Update ethereum-serai to ff 0.13
This commit is contained in:
Luke Parker
2023-03-28 04:38:01 -04:00
committed by GitHub
parent a9f6300e86
commit 79aff5d4c8
59 changed files with 865 additions and 429 deletions

View File

@@ -1,109 +1,183 @@
use rand_core::RngCore;
use subtle::Choice;
use group::ff::Field;
/// Perform basic tests on equality.
pub fn test_eq<F: Field>() {
let zero = F::zero();
let one = F::one();
let zero = F::ZERO;
let one = F::ONE;
assert!(zero != one, "0 == 1");
assert!(!bool::from(zero.ct_eq(&one)), "0 ct_eq 1");
assert_eq!(zero, F::zero(), "0 != 0");
assert!(bool::from(zero.ct_eq(&F::zero())), "0 !ct_eq 0");
assert_eq!(zero, F::ZERO, "0 != 0");
assert!(bool::from(zero.ct_eq(&F::ZERO)), "0 !ct_eq 0");
assert_eq!(one, F::one(), "1 != 1");
assert!(bool::from(one.ct_eq(&F::one())), "1 !ct_eq 1");
assert_eq!(one, F::ONE, "1 != 1");
assert!(bool::from(one.ct_eq(&F::ONE)), "1 !ct_eq 1");
}
/// Verify conditional selection works. Doesn't verify it's actually constant time.
pub fn test_conditional_select<F: Field>() {
let zero = F::zero();
let one = F::one();
let zero = F::ZERO;
let one = F::ONE;
assert_eq!(F::conditional_select(&zero, &one, 0.into()), zero, "couldn't select when false");
assert_eq!(F::conditional_select(&zero, &one, 1.into()), one, "couldn't select when true");
}
/// Perform basic tests on addition.
pub fn test_add<F: Field>() {
assert_eq!(F::zero() + F::zero(), F::zero(), "0 + 0 != 0");
assert_eq!(F::zero() + F::one(), F::one(), "0 + 1 != 1");
assert_eq!(F::one() + F::zero(), F::one(), "1 + 0 != 1");
assert_eq!(F::ZERO + F::ZERO, F::ZERO, "0 + 0 != 0");
assert_eq!(F::ZERO + F::ONE, F::ONE, "0 + 1 != 1");
assert_eq!(F::ONE + F::ZERO, F::ONE, "1 + 0 != 1");
// Only PrimeField offers From<u64>
// Accordingly, we assume either double or addition is correct
// They either have to be matchingly correct or matchingly incorrect, yet we can't
// reliably determine that here
assert_eq!(F::one() + F::one(), F::one().double(), "1 + 1 != 2");
assert_eq!(F::ONE + F::ONE, F::ONE.double(), "1 + 1 != 2");
}
/// Perform basic tests on sum.
pub fn test_sum<F: Field>() {
assert_eq!((&[] as &[F]).iter().sum::<F>(), F::ZERO, "[].sum() != 0");
assert_eq!([F::ZERO].iter().sum::<F>(), F::ZERO, "[0].sum() != 0");
assert_eq!([F::ONE].iter().sum::<F>(), F::ONE, "[1].sum() != 1");
let two = F::ONE + F::ONE;
assert_eq!([F::ONE, F::ONE].iter().sum::<F>(), two, "[1, 1].sum() != 2");
assert_eq!([two, F::ONE].iter().sum::<F>(), two + F::ONE, "[2, 1].sum() != 3");
assert_eq!([two, F::ZERO, F::ONE].iter().sum::<F>(), two + F::ONE, "[2, 0, 1].sum() != 3");
}
/// Perform basic tests on subtraction.
pub fn test_sub<F: Field>() {
assert_eq!(F::zero() - F::zero(), F::zero(), "0 - 0 != 0");
assert_eq!(F::one() - F::zero(), F::one(), "1 - 0 != 1");
assert_eq!(F::one() - F::one(), F::zero(), "1 - 1 != 0");
#[allow(clippy::eq_op)]
let expr = F::ZERO - F::ZERO;
assert_eq!(expr, F::ZERO, "0 - 0 != 0");
assert_eq!(F::ONE - F::ZERO, F::ONE, "1 - 0 != 1");
#[allow(clippy::eq_op)]
let expr = F::ONE - F::ONE;
assert_eq!(expr, F::ZERO, "1 - 1 != 0");
}
/// Perform basic tests on negation.
pub fn test_neg<F: Field>() {
assert_eq!(-F::zero(), F::zero(), "-0 != 0");
assert_eq!(-(-F::one()), F::one(), "-(-1) != 1");
assert_eq!(F::one() + (-F::one()), F::zero(), "1 + -1 != 0");
assert_eq!(F::one() - (-F::one()), F::one().double(), "1 - -1 != 2");
assert_eq!(-F::ZERO, F::ZERO, "-0 != 0");
assert_eq!(-(-F::ONE), F::ONE, "-(-1) != 1");
assert_eq!(F::ONE + (-F::ONE), F::ZERO, "1 + -1 != 0");
assert_eq!(F::ONE - (-F::ONE), F::ONE.double(), "1 - -1 != 2");
}
/// Perform basic tests on multiplication.
pub fn test_mul<F: Field>() {
assert_eq!(F::zero() * F::zero(), F::zero(), "0 * 0 != 0");
assert_eq!(F::one() * F::zero(), F::zero(), "1 * 0 != 0");
assert_eq!(F::one() * F::one(), F::one(), "1 * 1 != 1");
let two = F::one().double();
assert_eq!(two * (two + F::one()), two + two + two, "2 * 3 != 6");
assert_eq!(F::ZERO * F::ZERO, F::ZERO, "0 * 0 != 0");
assert_eq!(F::ONE * F::ZERO, F::ZERO, "1 * 0 != 0");
assert_eq!(F::ONE * F::ONE, F::ONE, "1 * 1 != 1");
let two = F::ONE.double();
assert_eq!(two * (two + F::ONE), two + two + two, "2 * 3 != 6");
}
/// Perform basic tests on product.
pub fn test_product<F: Field>() {
assert_eq!((&[] as &[F]).iter().product::<F>(), F::ONE, "[].product() != 1");
assert_eq!([F::ZERO].iter().product::<F>(), F::ZERO, "[0].product() != 0");
assert_eq!([F::ONE].iter().product::<F>(), F::ONE, "[1].product() != 1");
assert_eq!([F::ONE, F::ONE].iter().product::<F>(), F::ONE, "[1, 1].product() != 2");
let two = F::ONE + F::ONE;
assert_eq!([two, F::ONE].iter().product::<F>(), two, "[2, 1].product() != 2");
assert_eq!([two, two].iter().product::<F>(), two + two, "[2, 2].product() != 4");
assert_eq!([two, two, F::ONE].iter().product::<F>(), two + two, "[2, 2, 1].product() != 4");
assert_eq!([two, F::ZERO, F::ONE].iter().product::<F>(), F::ZERO, "[2, 0, 1].product() != 0");
}
/// Perform basic tests on the square function.
pub fn test_square<F: Field>() {
assert_eq!(F::zero().square(), F::zero(), "0^2 != 0");
assert_eq!(F::one().square(), F::one(), "1^2 != 1");
let two = F::one().double();
assert_eq!(F::ZERO.square(), F::ZERO, "0^2 != 0");
assert_eq!(F::ONE.square(), F::ONE, "1^2 != 1");
let two = F::ONE.double();
assert_eq!(two.square(), two + two, "2^2 != 4");
let three = two + F::one();
let three = two + F::ONE;
assert_eq!(three.square(), three * three, "3^2 != 9");
}
/// Perform basic tests on the invert function.
pub fn test_invert<F: Field>() {
assert!(bool::from(F::zero().invert().is_none()), "0.invert() is some");
assert_eq!(F::one().invert().unwrap(), F::one(), "1.invert() != 1");
assert!(bool::from(F::ZERO.invert().is_none()), "0.invert() is some");
assert_eq!(F::ONE.invert().unwrap(), F::ONE, "1.invert() != 1");
let two = F::one().double();
let three = two + F::one();
let two = F::ONE.double();
let three = two + F::ONE;
assert_eq!(two * three.invert().unwrap() * three, two, "2 * 3.invert() * 3 != 2");
}
/// Perform basic tests on the sqrt function.
/// Perform basic tests on the sqrt functions.
pub fn test_sqrt<F: Field>() {
assert_eq!(F::zero().sqrt().unwrap(), F::zero(), "sqrt(0) != 0");
assert_eq!(F::one().sqrt().unwrap(), F::one(), "sqrt(1) != 1");
assert_eq!(F::ZERO.sqrt().unwrap(), F::ZERO, "sqrt(0) != 0");
assert!(
(F::ONE.sqrt().unwrap() == F::ONE) || (F::ONE.sqrt().unwrap() == -F::ONE),
"sqrt(1) != 1"
);
let mut has_root = F::one().double();
let mut has_root = F::ONE.double();
while bool::from(has_root.sqrt().is_none()) {
has_root += F::one();
has_root += F::ONE;
}
// The following code doesn't assume which root is returned, yet it does assume a consistent root
// is returned
let root = has_root.sqrt().unwrap();
assert_eq!(root * root, has_root, "sqrt(x)^2 != x");
let check = |value: (_, _), expected: (_, F), msg| {
assert_eq!(bool::from(value.0), bool::from(expected.0), "{}", msg);
assert!((value.1 == expected.1) || (value.1 == -expected.1), "{}", msg);
};
check(
F::sqrt_ratio(&has_root, &F::ONE),
(Choice::from(1), root),
"sqrt_ratio didn't return the root with a divisor of 1",
);
check(
F::sqrt_ratio(&(has_root * F::ONE.double()), &F::ONE.double()),
(Choice::from(1), root),
"sqrt_ratio didn't return the root with a divisor of 2",
);
check(F::sqrt_alt(&F::ZERO), F::sqrt_ratio(&F::ZERO, &F::ONE), "sqrt_alt(0) != sqrt_ratio(0, 1)");
check(F::sqrt_alt(&F::ONE), F::sqrt_ratio(&F::ONE, &F::ONE), "sqrt_alt(1) != sqrt_ratio(1, 1)");
check(F::sqrt_alt(&has_root), (Choice::from(1), root), "sqrt_alt(square) != (1, root)");
// Check 0 divisors are properly implemented
check(
F::sqrt_ratio(&has_root, &F::ZERO),
(Choice::from(0), F::ZERO),
"sqrt_ratio didn't return the right value for a 0 divisor",
);
// Check non-squares are appropriately marked
let mut no_root = has_root + F::ONE;
while bool::from(no_root.sqrt().is_some()) {
no_root += F::ONE;
}
assert!(
!bool::from(F::sqrt_ratio(&no_root, &F::ONE).0),
"sqrt_ratio claimed non-square had root"
);
assert!(!bool::from(F::sqrt_alt(&no_root).0), "sqrt_alt claimed non-square had root");
}
/// Perform basic tests on the is_zero functions.
pub fn test_is_zero<F: Field>() {
assert!(bool::from(F::zero().is_zero()), "0 is not 0");
assert!(F::zero().is_zero_vartime(), "0 is not 0");
assert!(bool::from(F::ZERO.is_zero()), "0 is not 0");
assert!(F::ZERO.is_zero_vartime(), "0 is not 0");
}
/// Perform basic tests on the cube function.
pub fn test_cube<F: Field>() {
assert_eq!(F::zero().cube(), F::zero(), "0^3 != 0");
assert_eq!(F::one().cube(), F::one(), "1^3 != 1");
let two = F::one().double();
assert_eq!(F::ZERO.cube(), F::ZERO, "0^3 != 0");
assert_eq!(F::ONE.cube(), F::ONE, "1^3 != 1");
let two = F::ONE.double();
assert_eq!(two.cube(), two * two * two, "2^3 != 8");
}
@@ -130,14 +204,22 @@ pub fn test_random<R: RngCore, F: Field>(rng: &mut R) {
pub fn test_field<R: RngCore, F: Field>(rng: &mut R) {
test_eq::<F>();
test_conditional_select::<F>();
test_add::<F>();
test_sum::<F>();
test_sub::<F>();
test_neg::<F>();
test_mul::<F>();
test_product::<F>();
test_square::<F>();
test_invert::<F>();
test_sqrt::<F>();
test_is_zero::<F>();
test_cube::<F>();
test_random::<R, F>(rng);
}

View File

@@ -108,7 +108,7 @@ pub fn test_mul<G: Group>() {
/// Test `((order - 1) * G) + G == identity`.
pub fn test_order<G: Group>() {
let minus_one = G::generator() * (G::Scalar::zero() - G::Scalar::one());
let minus_one = G::generator() * (G::Scalar::ZERO - G::Scalar::ONE);
assert!(minus_one != G::identity(), "(modulus - 1) * G was identity");
assert_eq!(minus_one + G::generator(), G::identity(), "((modulus - 1) * G) + G wasn't identity");
}
@@ -203,3 +203,15 @@ fn test_k256() {
fn test_p256() {
test_prime_group_bits::<_, p256::ProjectivePoint>(&mut rand_core::OsRng);
}
#[test]
fn test_bls12_381() {
test_prime_group_bits::<_, bls12_381::G1Projective>(&mut rand_core::OsRng);
test_prime_group_bits::<_, bls12_381::G2Projective>(&mut rand_core::OsRng);
}
#[test]
fn test_pallas_vesta() {
test_prime_group_bits::<_, pasta_curves::pallas::Point>(&mut rand_core::OsRng);
test_prime_group_bits::<_, pasta_curves::vesta::Point>(&mut rand_core::OsRng);
}

View File

@@ -6,17 +6,28 @@ use crate::field::test_field;
// Ideally, this and test_one would be under Field, yet these tests require access to From<u64>
/// Test zero returns F::from(0).
pub fn test_zero<F: PrimeField>() {
assert_eq!(F::zero(), F::from(0u64), "0 != 0");
assert_eq!(F::ZERO, F::from(0u64), "0 != 0");
}
/// Test one returns F::from(1).
pub fn test_one<F: PrimeField>() {
assert_eq!(F::one(), F::from(1u64), "1 != 1");
assert_eq!(F::ONE, F::from(1u64), "1 != 1");
}
/// Test `From<u64>` for F works.
pub fn test_from_u64<F: PrimeField>() {
assert_eq!(F::one().double(), F::from(2u64), "2 != 2");
assert_eq!(F::ZERO, F::from(0u64), "0 != 0u64");
assert_eq!(F::ONE, F::from(1u64), "1 != 1u64");
assert_eq!(F::ONE.double(), F::from(2u64), "2 != 2u64");
assert_eq!(F::ONE.double() + F::ONE, F::from(3u64), "3 != 3u64");
}
/// Test from_u128 for F works.
pub fn test_from_u128<F: PrimeField>() {
assert_eq!(F::ZERO, F::from_u128(0u128), "0 != 0u128");
assert_eq!(F::ONE, F::from_u128(1u128), "1 != 1u128");
assert_eq!(F::from(2u64), F::from_u128(2u128), "2u64 != 2u128");
assert_eq!(F::from(3u64), F::from_u128(3u128), "3u64 != 3u128");
}
/// Test is_odd/is_even works.
@@ -24,14 +35,14 @@ pub fn test_from_u64<F: PrimeField>() {
/// Accordingly, this test doesn't support fields alternatively defined.
/// TODO: Improve in the future.
pub fn test_is_odd<F: PrimeField>() {
assert_eq!(F::zero().is_odd().unwrap_u8(), 0, "0 was odd");
assert_eq!(F::zero().is_even().unwrap_u8(), 1, "0 wasn't even");
assert_eq!(F::ZERO.is_odd().unwrap_u8(), 0, "0 was odd");
assert_eq!(F::ZERO.is_even().unwrap_u8(), 1, "0 wasn't even");
assert_eq!(F::one().is_odd().unwrap_u8(), 1, "1 was even");
assert_eq!(F::one().is_even().unwrap_u8(), 0, "1 wasn't odd");
assert_eq!(F::ONE.is_odd().unwrap_u8(), 1, "1 was even");
assert_eq!(F::ONE.is_even().unwrap_u8(), 0, "1 wasn't odd");
// Make sure an odd value added to an odd value is even
let two = F::one().double();
let two = F::ONE.double();
assert_eq!(two.is_odd().unwrap_u8(), 0, "2 was odd");
assert_eq!(two.is_even().unwrap_u8(), 1, "2 wasn't even");
@@ -40,7 +51,7 @@ pub fn test_is_odd<F: PrimeField>() {
assert_eq!(four.is_odd().unwrap_u8(), 0, "4 was odd");
assert_eq!(four.is_even().unwrap_u8(), 1, "4 wasn't even");
let neg_one = -F::one();
let neg_one = -F::ONE;
assert_eq!(neg_one.is_odd().unwrap_u8(), 0, "-1 was odd");
assert_eq!(neg_one.is_even().unwrap_u8(), 1, "-1 wasn't even");
@@ -66,13 +77,13 @@ pub fn test_encoding<F: PrimeField>() {
"canonical encoding decoded produced distinct encoding"
);
};
test(F::zero(), "0");
test(F::one(), "1");
test(F::one() + F::one(), "2");
test(-F::one(), "-1");
test(F::ZERO, "0");
test(F::ONE, "1");
test(F::ONE + F::ONE, "2");
test(-F::ONE, "-1");
// Also check if a non-canonical encoding is possible
let mut high = (F::zero() - F::one()).to_repr();
let mut high = (F::ZERO - F::ONE).to_repr();
let mut possible_non_canon = false;
for byte in high.as_mut() {
// The fact a bit isn't set in the highest possible value suggests there's unused bits
@@ -97,6 +108,7 @@ pub fn test_prime_field<R: RngCore, F: PrimeField>(rng: &mut R) {
test_zero::<F>();
test_one::<F>();
test_from_u64::<F>();
test_from_u128::<F>();
test_is_odd::<F>();
// Do a sanity check on the CAPACITY. A full test can't be done at this time
@@ -109,12 +121,12 @@ pub fn test_prime_field<R: RngCore, F: PrimeField>(rng: &mut R) {
// This test assumes that the modulus is at least 4.
pub fn test_to_le_bits<F: PrimeField + PrimeFieldBits>() {
{
let bits = F::zero().to_le_bits();
let bits = F::ZERO.to_le_bits();
assert_eq!(bits.iter().filter(|bit| **bit).count(), 0, "0 had bits set");
}
{
let bits = F::one().to_le_bits();
let bits = F::ONE.to_le_bits();
assert!(bits[0], "1 didn't have its least significant bit set");
assert_eq!(bits.iter().filter(|bit| **bit).count(), 1, "1 had multiple bits set");
}
@@ -140,20 +152,20 @@ pub fn test_char_le_bits<F: PrimeField + PrimeFieldBits>() {
assert!(F::char_le_bits().iter().any(|bit| *bit), "char_le_bits contained 0");
// Test this is the bit pattern of the modulus by reconstructing the modulus from it
let mut bit = F::one();
let mut modulus = F::zero();
let mut bit = F::ONE;
let mut modulus = F::ZERO;
for set in F::char_le_bits() {
if set {
modulus += bit;
}
bit = bit.double();
}
assert_eq!(modulus, F::zero(), "char_le_bits did not contain the field's modulus");
assert_eq!(modulus, F::ZERO, "char_le_bits did not contain the field's modulus");
}
/// Test NUM_BITS is accurate.
pub fn test_num_bits<F: PrimeField + PrimeFieldBits>() {
let mut val = F::one();
let mut val = F::ONE;
let mut bit = 0;
while ((bit + 1) < val.to_le_bits().len()) && val.double().to_le_bits()[bit + 1] {
val = val.double();
@@ -171,11 +183,11 @@ pub fn test_num_bits<F: PrimeField + PrimeFieldBits>() {
pub fn test_capacity<F: PrimeField + PrimeFieldBits>() {
assert!(F::CAPACITY <= F::NUM_BITS, "capacity exceeded number of bits");
let mut val = F::one();
let mut val = F::ONE;
assert!(val.to_le_bits()[0], "1 didn't have its least significant bit set");
for b in 1 .. F::CAPACITY {
val = val.double();
val += F::one();
val += F::ONE;
for i in 0 ..= b {
assert!(
val.to_le_bits()[usize::try_from(i).unwrap()],
@@ -192,7 +204,7 @@ pub fn test_capacity<F: PrimeField + PrimeFieldBits>() {
F::CAPACITY,
"field has a power of two modulus yet CAPACITY doesn't equal NUM_BITS",
);
assert_eq!(val + F::one(), F::zero());
assert_eq!(val + F::ONE, F::ZERO, "CAPACITY set bits, + 1, != zero for a binary field");
return;
}
@@ -200,7 +212,7 @@ pub fn test_capacity<F: PrimeField + PrimeFieldBits>() {
}
fn pow<F: PrimeFieldBits>(base: F, exp: F) -> F {
let mut res = F::one();
let mut res = F::ONE;
for bit in exp.to_le_bits().iter().rev() {
res *= res;
if *bit {
@@ -214,22 +226,20 @@ fn pow<F: PrimeFieldBits>(base: F, exp: F) -> F {
/// Perform basic tests on the pow functions, even when passed non-canonical inputs.
pub fn test_pow<F: PrimeFieldBits>() {
// Sanity check the local pow algorithm. Does not have assert messages as these shouldn't fail
assert_eq!(pow(F::one(), F::zero()), F::one());
assert_eq!(pow(F::one().double(), F::zero()), F::one());
assert_eq!(pow(F::one(), F::one()), F::one());
assert_eq!(pow(F::ONE, F::ZERO), F::ONE);
assert_eq!(pow(F::ONE.double(), F::ZERO), F::ONE);
assert_eq!(pow(F::ONE, F::ONE), F::ONE);
let two = F::one().double();
assert_eq!(pow(two, F::one()), two);
let two = F::ONE.double();
assert_eq!(pow(two, F::ONE), two);
assert_eq!(pow(two, two), two.double());
let three = two + F::one();
assert_eq!(pow(three, F::one()), three);
let three = two + F::ONE;
assert_eq!(pow(three, F::ONE), three);
assert_eq!(pow(three, two), three * three);
assert_eq!(pow(three, three), three * three * three);
// TODO: Test against Field::pow once updated to ff 0.13
// Choose a small base without a notably uniform bit pattern
let bit_0 = F::one();
let bit_0 = F::ONE;
let base = {
let bit_1 = bit_0.double();
let bit_2 = bit_1.double();
@@ -241,36 +251,53 @@ pub fn test_pow<F: PrimeFieldBits>() {
bit_7 + bit_6 + bit_5 + bit_2 + bit_0
};
// Ensure pow_vartime returns 1 when the base is raised to 0, handling malleated inputs
assert_eq!(base.pow_vartime([]), F::one(), "pow_vartime x^0 ([]) != 1");
assert_eq!(base.pow_vartime([0]), F::one(), "pow_vartime x^0 ([0]) != 1");
assert_eq!(base.pow_vartime([0, 0]), F::one(), "pow_vartime x^0 ([0, 0]) != 1");
// Ensure pow/pow_vartime return 1 when the base is raised to 0, handling malleated inputs
assert_eq!(base.pow([]), F::ONE, "pow x^0 ([]) != 1");
assert_eq!(base.pow_vartime([]), F::ONE, "pow x^0 ([]) != 1");
assert_eq!(base.pow([0]), F::ONE, "pow_vartime x^0 ([0]) != 1");
assert_eq!(base.pow_vartime([0]), F::ONE, "pow_vartime x^0 ([0]) != 1");
assert_eq!(base.pow([0, 0]), F::ONE, "pow x^0 ([0, 0]) != 1");
assert_eq!(base.pow_vartime([0, 0]), F::ONE, "pow_vartime x^0 ([0, 0]) != 1");
// Ensure pow_vartime returns the base when raised to 1, handling malleated inputs
assert_eq!(base.pow_vartime([1]), base, "pow_vartime x^1 ([1]) != x");
// Ensure pow/pow_vartime return the base when raised to 1, handling malleated inputs
assert_eq!(base.pow([1]), base, "pow x^1 ([1]) != x");
assert_eq!(base.pow_vartime([1, 0]), base, "pow_vartime x^1 ([1, 0]) != x");
assert_eq!(base.pow([1]), base, "pow x^1 ([1]) != x");
assert_eq!(base.pow_vartime([1, 0]), base, "pow_vartime x^1 ([1, 0]) != x");
// Ensure pow_vartime can handle multiple u64s properly
// Ensure pow/pow_vartime can handle multiple u64s properly
// Create a scalar which exceeds u64
let mut bit_64 = bit_0;
for _ in 0 .. 64 {
bit_64 = bit_64.double();
}
// Run the tests
assert_eq!(base.pow([0, 1]), pow(base, bit_64), "pow x^(2^64) != x^(2^64)");
assert_eq!(base.pow_vartime([0, 1]), pow(base, bit_64), "pow_vartime x^(2^64) != x^(2^64)");
assert_eq!(base.pow([1, 1]), pow(base, bit_64 + F::ONE), "pow x^(2^64 + 1) != x^(2^64 + 1)");
assert_eq!(
base.pow_vartime([1, 1]),
pow(base, bit_64 + F::one()),
pow(base, bit_64 + F::ONE),
"pow_vartime x^(2^64 + 1) != x^(2^64 + 1)"
);
}
/// Test the inverted constants are correct.
pub fn test_inv_consts<F: PrimeFieldBits>() {
assert_eq!(F::TWO_INV, F::from(2u64).invert().unwrap(), "F::TWO_INV != 2.invert()");
assert_eq!(
F::ROOT_OF_UNITY_INV,
F::ROOT_OF_UNITY.invert().unwrap(),
"F::ROOT_OF_UNITY_INV != F::ROOT_OF_UNITY.invert()"
);
}
/// Test S is correct.
pub fn test_s<F: PrimeFieldBits>() {
// "This is the number of leading zero bits in the little-endian bit representation of
// `modulus - 1`."
let mut s = 0;
for b in (F::zero() - F::one()).to_le_bits() {
for b in (F::ZERO - F::ONE).to_le_bits() {
if b {
break;
}
@@ -285,14 +312,14 @@ pub fn test_root_of_unity<F: PrimeFieldBits>() {
// `t = (modulus - 1) >> Self::S`."
// Get the bytes to shift
let mut bits = (F::zero() - F::one()).to_le_bits().iter().map(|bit| *bit).collect::<Vec<_>>();
let mut bits = (F::ZERO - F::ONE).to_le_bits().iter().map(|bit| *bit).collect::<Vec<_>>();
for _ in 0 .. F::S {
bits.remove(0);
}
// Construct t
let mut bit = F::one();
let mut t = F::zero();
let mut bit = F::ONE;
let mut t = F::ZERO;
for set in bits {
if set {
t += bit;
@@ -301,11 +328,20 @@ pub fn test_root_of_unity<F: PrimeFieldBits>() {
}
assert!(bool::from(t.is_odd()), "t wasn't odd");
assert_eq!(pow(F::multiplicative_generator(), t), F::root_of_unity(), "incorrect root of unity");
assert_eq!(pow(F::MULTIPLICATIVE_GENERATOR, t), F::ROOT_OF_UNITY, "incorrect root of unity");
assert_eq!(
pow(F::root_of_unity(), pow(F::from(2u64), F::from(F::S.into()))),
F::one(),
"root of unity raised to 2^S wasn't 1"
pow(F::ROOT_OF_UNITY, pow(F::from(2u64), F::from(F::S.into()))),
F::ONE,
"root of unity raised to 2^S wasn't 1",
);
}
/// Test DELTA is correct.
pub fn test_delta<F: PrimeFieldBits>() {
assert_eq!(
pow(F::MULTIPLICATIVE_GENERATOR, pow(F::from(2u64), F::from(u64::from(F::S)))),
F::DELTA,
"F::DELTA is incorrect"
);
}
@@ -317,8 +353,11 @@ pub fn test_prime_field_bits<R: RngCore, F: PrimeFieldBits>(rng: &mut R) {
test_char_le_bits::<F>();
test_pow::<F>();
test_inv_consts::<F>();
test_s::<F>();
test_root_of_unity::<F>();
test_delta::<F>();
test_num_bits::<F>();
test_capacity::<F>();