mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add a dedicated crate for testing ff/group implementors
Provides extensive testing for dalek-ff-group and ed448. Also includes a fix for an observed bug in ed448.
This commit is contained in:
122
crypto/ff-group-tests/src/field.rs
Normal file
122
crypto/ff-group-tests/src/field.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use group::ff::Field;
|
||||
|
||||
/// Perform basic tests on equality.
|
||||
pub fn test_eq<F: Field>() {
|
||||
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!(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();
|
||||
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");
|
||||
// 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");
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
/// 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!(two.square(), two + two, "2^2 != 4");
|
||||
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");
|
||||
|
||||
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.
|
||||
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");
|
||||
|
||||
let mut has_root = F::one().double();
|
||||
while bool::from(has_root.sqrt().is_none()) {
|
||||
has_root += F::one();
|
||||
}
|
||||
let root = has_root.sqrt().unwrap();
|
||||
assert_eq!(root * root, has_root, "sqrt(x)^2 != x");
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
/// 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!(two.cube(), two * two * two, "2^3 != 8");
|
||||
}
|
||||
|
||||
/// Run all tests on fields implementing Field.
|
||||
pub fn test_field<F: Field>() {
|
||||
test_eq::<F>();
|
||||
test_conditional_select::<F>();
|
||||
test_add::<F>();
|
||||
test_sub::<F>();
|
||||
test_neg::<F>();
|
||||
test_mul::<F>();
|
||||
test_square::<F>();
|
||||
test_invert::<F>();
|
||||
test_sqrt::<F>();
|
||||
test_is_zero::<F>();
|
||||
test_cube::<F>();
|
||||
}
|
||||
169
crypto/ff-group-tests/src/group.rs
Normal file
169
crypto/ff-group-tests/src/group.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
use group::{
|
||||
ff::{Field, PrimeFieldBits},
|
||||
Group,
|
||||
prime::PrimeGroup,
|
||||
};
|
||||
|
||||
use crate::prime_field::{test_prime_field, test_prime_field_bits};
|
||||
|
||||
/// Test equality.
|
||||
pub fn test_eq<G: Group>() {
|
||||
assert_eq!(G::identity(), G::identity(), "identity != identity");
|
||||
assert_eq!(G::generator(), G::generator(), "generator != generator");
|
||||
assert!(G::identity() != G::generator(), "identity != generator");
|
||||
}
|
||||
|
||||
/// Test identity.
|
||||
pub fn test_identity<G: Group>() {
|
||||
assert!(bool::from(G::identity().is_identity()), "identity wasn't identity");
|
||||
assert!(
|
||||
bool::from((G::identity() + G::identity()).is_identity()),
|
||||
"identity + identity wasn't identity"
|
||||
);
|
||||
assert!(
|
||||
bool::from((G::generator() - G::generator()).is_identity()),
|
||||
"generator - generator wasn't identity"
|
||||
);
|
||||
assert!(!bool::from(G::generator().is_identity()), "is_identity claimed generator was identity");
|
||||
}
|
||||
|
||||
/// Sanity check the generator.
|
||||
pub fn test_generator<G: Group>() {
|
||||
assert!(G::generator() != G::identity(), "generator was identity");
|
||||
assert!(
|
||||
(G::generator() + G::generator()) != G::generator(),
|
||||
"generator added to itself identity"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test doubling of group elements.
|
||||
pub fn test_double<G: Group>() {
|
||||
assert!(bool::from(G::identity().double().is_identity()), "identity.double() wasn't identity");
|
||||
assert_eq!(
|
||||
G::generator() + G::generator(),
|
||||
G::generator().double(),
|
||||
"generator + generator != generator.double()"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test addition.
|
||||
pub fn test_add<G: Group>() {
|
||||
assert_eq!(G::identity() + G::identity(), G::identity(), "identity + identity != identity");
|
||||
assert_eq!(G::identity() + G::generator(), G::generator(), "identity + generator != generator");
|
||||
assert_eq!(G::generator() + G::identity(), G::generator(), "generator + identity != generator");
|
||||
|
||||
let two = G::generator().double();
|
||||
assert_eq!(G::generator() + G::generator(), two, "generator + generator != two");
|
||||
let four = two.double();
|
||||
assert_eq!(
|
||||
G::generator() + G::generator() + G::generator() + G::generator(),
|
||||
four,
|
||||
"generator + generator + generator + generator != four"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test summation.
|
||||
pub fn test_sum<G: Group>() {
|
||||
assert_eq!(
|
||||
[G::generator(), G::generator()].iter().sum::<G>(),
|
||||
G::generator().double(),
|
||||
"[generator, generator].sum() != two"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test negation.
|
||||
pub fn test_neg<G: Group>() {
|
||||
assert_eq!(G::identity(), G::identity().neg(), "identity != -identity");
|
||||
assert_eq!(
|
||||
G::generator() + G::generator().neg(),
|
||||
G::identity(),
|
||||
"generator + -generator != identity"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test subtraction.
|
||||
pub fn test_sub<G: Group>() {
|
||||
assert_eq!(G::generator() - G::generator(), G::identity(), "generator - generator != identity");
|
||||
let two = G::generator() + G::generator();
|
||||
assert_eq!(two - G::generator(), G::generator(), "two - one != one");
|
||||
}
|
||||
|
||||
/// Test scalar multiplication
|
||||
pub fn test_mul<G: Group>() {
|
||||
assert_eq!(G::generator() * G::Scalar::from(0), G::identity(), "generator * 0 != identity");
|
||||
assert_eq!(G::generator() * G::Scalar::from(1), G::generator(), "generator * 1 != generator");
|
||||
assert_eq!(
|
||||
G::generator() * G::Scalar::from(2),
|
||||
G::generator() + G::generator(),
|
||||
"generator * 2 != generator + generator"
|
||||
);
|
||||
assert_eq!(G::identity() * G::Scalar::from(2), G::identity(), "identity * 2 != identity");
|
||||
}
|
||||
|
||||
/// Test `((order - 1) * G) + G == identity`.
|
||||
pub fn test_order<G: Group>() {
|
||||
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");
|
||||
}
|
||||
|
||||
/// Run all tests on groups implementing Group.
|
||||
pub fn test_group<G: Group>() {
|
||||
test_prime_field::<G::Scalar>();
|
||||
|
||||
test_eq::<G>();
|
||||
test_identity::<G>();
|
||||
test_generator::<G>();
|
||||
test_double::<G>();
|
||||
test_add::<G>();
|
||||
test_sum::<G>();
|
||||
test_neg::<G>();
|
||||
test_sub::<G>();
|
||||
test_mul::<G>();
|
||||
test_order::<G>();
|
||||
}
|
||||
|
||||
/// Test encoding and decoding of group elements.
|
||||
pub fn test_encoding<G: PrimeGroup>() {
|
||||
let test = |point: G, msg| {
|
||||
let bytes = point.to_bytes();
|
||||
let mut repr = G::Repr::default();
|
||||
repr.as_mut().copy_from_slice(bytes.as_ref());
|
||||
assert_eq!(point, G::from_bytes(&repr).unwrap(), "{} couldn't be encoded and decoded", msg);
|
||||
assert_eq!(
|
||||
point,
|
||||
G::from_bytes_unchecked(&repr).unwrap(),
|
||||
"{} couldn't be encoded and decoded",
|
||||
msg
|
||||
);
|
||||
};
|
||||
test(G::identity(), "identity");
|
||||
test(G::generator(), "generator");
|
||||
test(G::generator() + G::generator(), "(generator * 2)");
|
||||
}
|
||||
|
||||
/// Run all tests on groups implementing PrimeGroup (Group + GroupEncoding).
|
||||
pub fn test_prime_group<G: PrimeGroup>() {
|
||||
test_group::<G>();
|
||||
|
||||
test_encoding::<G>();
|
||||
}
|
||||
|
||||
/// Run all tests offered by this crate on the group.
|
||||
pub fn test_prime_group_bits<G: PrimeGroup>()
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
test_prime_field_bits::<G::Scalar>();
|
||||
test_prime_group::<G>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_k256_group_encoding() {
|
||||
test_prime_group_bits::<k256::ProjectivePoint>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_p256_group_encoding() {
|
||||
test_prime_group_bits::<p256::ProjectivePoint>();
|
||||
}
|
||||
9
crypto/ff-group-tests/src/lib.rs
Normal file
9
crypto/ff-group-tests/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
/// Tests for the Field trait.
|
||||
pub mod field;
|
||||
/// Tests for the PrimeField and PrimeFieldBits traits.
|
||||
pub mod prime_field;
|
||||
|
||||
/// Tests for the Group and GroupEncoding traits.
|
||||
pub mod group;
|
||||
291
crypto/ff-group-tests/src/prime_field.rs
Normal file
291
crypto/ff-group-tests/src/prime_field.rs
Normal file
@@ -0,0 +1,291 @@
|
||||
use group::ff::{PrimeField, PrimeFieldBits};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/// Test one returns F::from(1).
|
||||
pub fn test_one<F: PrimeField>() {
|
||||
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");
|
||||
}
|
||||
|
||||
/// Test is_odd/is_even works.
|
||||
/// This test assumes an odd modulus with oddness being determined by the least-significant bit.
|
||||
/// 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::one().is_odd().unwrap_u8(), 1, "1 was even");
|
||||
assert_eq!(F::one().is_even().unwrap_u8(), 0, "1 wasn't odd");
|
||||
|
||||
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");
|
||||
|
||||
assert_eq!(neg_one.double().is_odd().unwrap_u8(), 1, "(-1).double() was even");
|
||||
assert_eq!(neg_one.double().is_even().unwrap_u8(), 0, "(-1).double() wasn't odd");
|
||||
}
|
||||
|
||||
/// Test encoding and decoding of field elements.
|
||||
pub fn test_encoding<F: PrimeField>() {
|
||||
let test = |scalar: F, msg| {
|
||||
let bytes = scalar.to_repr();
|
||||
let mut repr = F::Repr::default();
|
||||
repr.as_mut().copy_from_slice(bytes.as_ref());
|
||||
assert_eq!(scalar, F::from_repr(repr).unwrap(), "{} couldn't be encoded and decoded", msg);
|
||||
assert_eq!(
|
||||
scalar,
|
||||
F::from_repr_vartime(repr).unwrap(),
|
||||
"{} couldn't be encoded and decoded",
|
||||
msg
|
||||
);
|
||||
};
|
||||
test(F::zero(), "0");
|
||||
test(F::one(), "1");
|
||||
test(F::one() + F::one(), "2");
|
||||
test(-F::one(), "-1");
|
||||
}
|
||||
|
||||
/// Run all tests on fields implementing PrimeField.
|
||||
pub fn test_prime_field<F: PrimeField>() {
|
||||
test_field::<F>();
|
||||
|
||||
test_zero::<F>();
|
||||
test_one::<F>();
|
||||
test_from_u64::<F>();
|
||||
test_is_odd::<F>();
|
||||
|
||||
// Do a sanity check on the CAPACITY. A full test can't be done at this time
|
||||
assert!(F::CAPACITY <= F::NUM_BITS, "capacity exceeded number of bits");
|
||||
|
||||
test_encoding::<F>();
|
||||
}
|
||||
|
||||
/// Test to_le_bits returns the little-endian bits of a value.
|
||||
// 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();
|
||||
assert_eq!(bits.iter().filter(|bit| **bit).count(), 0, "0 had bits set");
|
||||
}
|
||||
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
{
|
||||
let bits = F::from(2).to_le_bits();
|
||||
assert!(bits[1], "2 didn't have its second bit set");
|
||||
assert_eq!(bits.iter().filter(|bit| **bit).count(), 1, "2 had multiple bits set");
|
||||
}
|
||||
|
||||
{
|
||||
let bits = F::from(3).to_le_bits();
|
||||
assert!(bits[0], "3 didn't have its first bit set");
|
||||
assert!(bits[1], "3 didn't have its second bit set");
|
||||
assert_eq!(bits.iter().filter(|bit| **bit).count(), 2, "2 didn't have two bits set");
|
||||
}
|
||||
}
|
||||
|
||||
/// Test char_le_bits returns the bits of the modulus.
|
||||
pub fn test_char_le_bits<F: PrimeField + PrimeFieldBits>() {
|
||||
// A field with a modulus of 0 may be technically valid? Yet these tests assume some basic
|
||||
// functioning.
|
||||
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();
|
||||
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");
|
||||
}
|
||||
|
||||
/// Test NUM_BITS is accurate.
|
||||
pub fn test_num_bits<F: PrimeField + PrimeFieldBits>() {
|
||||
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();
|
||||
bit += 1;
|
||||
}
|
||||
assert_eq!(
|
||||
F::NUM_BITS,
|
||||
u32::try_from(bit + 1).unwrap(),
|
||||
"NUM_BITS was incorrect. it should be {}",
|
||||
bit + 1
|
||||
);
|
||||
}
|
||||
|
||||
/// Test CAPACITY is accurate.
|
||||
pub fn test_capacity<F: PrimeField + PrimeFieldBits>() {
|
||||
assert!(F::CAPACITY <= F::NUM_BITS, "capacity exceeded number of bits");
|
||||
|
||||
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();
|
||||
for i in 0 ..= b {
|
||||
assert!(
|
||||
val.to_le_bits()[usize::try_from(i).unwrap()],
|
||||
"couldn't set a bit within the capacity",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If the field has a modulus which is a power of 2, NUM_BITS should equal CAPACITY
|
||||
// Adding one would also be sufficient to trigger an overflow
|
||||
if F::char_le_bits().iter().filter(|bit| **bit).count() == 1 {
|
||||
assert_eq!(
|
||||
F::NUM_BITS,
|
||||
F::CAPACITY,
|
||||
"field has a power of two modulus yet CAPACITY doesn't equal NUM_BITS",
|
||||
);
|
||||
assert_eq!(val + F::one(), F::zero());
|
||||
return;
|
||||
}
|
||||
|
||||
assert_eq!(F::NUM_BITS - 1, F::CAPACITY, "capacity wasn't NUM_BITS - 1");
|
||||
}
|
||||
|
||||
fn pow<F: PrimeFieldBits>(base: F, exp: F) -> F {
|
||||
let mut res = F::one();
|
||||
for bit in exp.to_le_bits().iter().rev() {
|
||||
res *= res;
|
||||
if *bit {
|
||||
res *= base;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
// Ideally, this would be under field.rs, yet the above pow function requires PrimeFieldBits
|
||||
/// 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());
|
||||
|
||||
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);
|
||||
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 base = {
|
||||
let bit_1 = bit_0.double();
|
||||
let bit_2 = bit_1.double();
|
||||
let bit_3 = bit_2.double();
|
||||
let bit_4 = bit_3.double();
|
||||
let bit_5 = bit_4.double();
|
||||
let bit_6 = bit_5.double();
|
||||
let bit_7 = bit_6.double();
|
||||
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_vartime returns the base when raised to 1, handling malleated inputs
|
||||
assert_eq!(base.pow_vartime([1]), base, "pow_vartime 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
|
||||
// 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_vartime([0, 1]), pow(base, bit_64), "pow_vartime x^(2^64) != x^(2^64)");
|
||||
assert_eq!(
|
||||
base.pow_vartime([1, 1]),
|
||||
pow(base, bit_64 + F::one()),
|
||||
"pow_vartime x^(2^64 + 1) != x^(2^64 + 1)"
|
||||
);
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
if b {
|
||||
break;
|
||||
}
|
||||
s += 1;
|
||||
}
|
||||
assert_eq!(s, F::S, "incorrect S");
|
||||
}
|
||||
|
||||
// Test the root of unity is correct for the given multiplicative generator.
|
||||
pub fn test_root_of_unity<F: PrimeFieldBits>() {
|
||||
// "It can be calculated by exponentiating `Self::multiplicative_generator` by `t`, where
|
||||
// `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<_>>();
|
||||
for _ in 0 .. F::S {
|
||||
bits.remove(0);
|
||||
}
|
||||
|
||||
// Construct t
|
||||
let mut bit = F::one();
|
||||
let mut t = F::zero();
|
||||
for set in bits {
|
||||
if set {
|
||||
t += bit;
|
||||
}
|
||||
bit = bit.double();
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
||||
/// Run all tests on fields implementing PrimeFieldBits.
|
||||
pub fn test_prime_field_bits<F: PrimeFieldBits>() {
|
||||
test_prime_field::<F>();
|
||||
|
||||
test_to_le_bits::<F>();
|
||||
test_char_le_bits::<F>();
|
||||
|
||||
test_pow::<F>();
|
||||
test_s::<F>();
|
||||
test_root_of_unity::<F>();
|
||||
|
||||
test_num_bits::<F>();
|
||||
test_capacity::<F>();
|
||||
}
|
||||
Reference in New Issue
Block a user