mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 03:51:22 +00:00
1063 lines
26 KiB
Rust
1063 lines
26 KiB
Rust
use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
|
|
use std::ops::{AddAssign, MulAssign, SubAssign};
|
|
|
|
#[derive(PrimeField)]
|
|
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
|
|
#[PrimeFieldGenerator = "7"]
|
|
pub struct Fr(FrRepr);
|
|
|
|
#[cfg(test)]
|
|
use ff::PowVartime;
|
|
#[cfg(test)]
|
|
use rand_core::SeedableRng;
|
|
#[cfg(test)]
|
|
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);
|
|
assert!(!a.is_valid());
|
|
a.0.sub_noborrow(&FrRepr::from(1));
|
|
assert!(a.is_valid());
|
|
assert!(Fr::from(0).is_valid());
|
|
assert!(Fr(FrRepr([
|
|
0xffffffff00000000,
|
|
0x53bda402fffe5bfe,
|
|
0x3339d80809a1d805,
|
|
0x73eda753299d7d48
|
|
]))
|
|
.is_valid());
|
|
assert!(!Fr(FrRepr([
|
|
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(FrRepr([
|
|
0x437ce7616d580765,
|
|
0xd42d1ccb29d1235b,
|
|
0xed8f753821bd1423,
|
|
0x4eede1c9c89528ca,
|
|
]));
|
|
assert!(tmp.is_valid());
|
|
// Test that adding zero has no effect.
|
|
tmp.add_assign(&Fr(FrRepr::from(0)));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
0x437ce7616d580765,
|
|
0xd42d1ccb29d1235b,
|
|
0xed8f753821bd1423,
|
|
0x4eede1c9c89528ca
|
|
]))
|
|
);
|
|
// Add one and test for the result.
|
|
tmp.add_assign(&Fr(FrRepr::from(1)));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
0x437ce7616d580766,
|
|
0xd42d1ccb29d1235b,
|
|
0xed8f753821bd1423,
|
|
0x4eede1c9c89528ca
|
|
]))
|
|
);
|
|
// Add another random number that exercises the reduction.
|
|
tmp.add_assign(&Fr(FrRepr([
|
|
0x946f435944f7dc79,
|
|
0xb55e7ee6533a9b9b,
|
|
0x1e43b84c2f6194ca,
|
|
0x58717ab525463496,
|
|
])));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
0xd7ec2abbb24fe3de,
|
|
0x35cdf7ae7d0d62f7,
|
|
0xd899557c477cd0e9,
|
|
0x3371b52bc43de018
|
|
]))
|
|
);
|
|
// Add one to (r - 1) and test for the result.
|
|
tmp = Fr(FrRepr([
|
|
0xffffffff00000000,
|
|
0x53bda402fffe5bfe,
|
|
0x3339d80809a1d805,
|
|
0x73eda753299d7d48,
|
|
]));
|
|
tmp.add_assign(&Fr(FrRepr::from(1)));
|
|
assert!(tmp.0.is_zero());
|
|
// Add a random number to another one such that the result is r - 1
|
|
tmp = Fr(FrRepr([
|
|
0xade5adacdccb6190,
|
|
0xaa21ee0f27db3ccd,
|
|
0x2550f4704ae39086,
|
|
0x591d1902e7c5ba27,
|
|
]));
|
|
tmp.add_assign(&Fr(FrRepr([
|
|
0x521a525223349e70,
|
|
0xa99bb5f3d8231f31,
|
|
0xde8e397bebe477e,
|
|
0x1ad08e5041d7c321,
|
|
])));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
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());
|
|
}
|
|
|
|
// 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(FrRepr([
|
|
0x6a68c64b6f735a2b,
|
|
0xd5f4d143fe0a1972,
|
|
0x37c17f3829267c62,
|
|
0xa2f37391f30915c,
|
|
]));
|
|
tmp.sub_assign(&Fr(FrRepr([
|
|
0xade5adacdccb6190,
|
|
0xaa21ee0f27db3ccd,
|
|
0x2550f4704ae39086,
|
|
0x591d1902e7c5ba27,
|
|
])));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
0xbc83189d92a7f89c,
|
|
0x7f908737d62d38a3,
|
|
0x45aa62cfe7e4c3e1,
|
|
0x24ffc5896108547d
|
|
]))
|
|
);
|
|
|
|
// Test the opposite subtraction which doesn't test reduction.
|
|
tmp = Fr(FrRepr([
|
|
0xade5adacdccb6190,
|
|
0xaa21ee0f27db3ccd,
|
|
0x2550f4704ae39086,
|
|
0x591d1902e7c5ba27,
|
|
]));
|
|
tmp.sub_assign(&Fr(FrRepr([
|
|
0x6a68c64b6f735a2b,
|
|
0xd5f4d143fe0a1972,
|
|
0x37c17f3829267c62,
|
|
0xa2f37391f30915c,
|
|
])));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
0x437ce7616d580765,
|
|
0xd42d1ccb29d1235b,
|
|
0xed8f753821bd1423,
|
|
0x4eede1c9c89528ca
|
|
]))
|
|
);
|
|
|
|
// Test for sensible results with zero
|
|
tmp = Fr(FrRepr::from(0));
|
|
tmp.sub_assign(&Fr(FrRepr::from(0)));
|
|
assert!(tmp.is_zero());
|
|
|
|
tmp = Fr(FrRepr([
|
|
0x437ce7616d580765,
|
|
0xd42d1ccb29d1235b,
|
|
0xed8f753821bd1423,
|
|
0x4eede1c9c89528ca,
|
|
]));
|
|
tmp.sub_assign(&Fr(FrRepr::from(0)));
|
|
assert_eq!(
|
|
tmp,
|
|
Fr(FrRepr([
|
|
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(FrRepr([
|
|
0x6b7e9b8faeefc81a,
|
|
0xe30a8463f348ba42,
|
|
0xeff3cb67a8279c9c,
|
|
0x3d303651bd7c774d,
|
|
]));
|
|
tmp.mul_assign(&Fr(FrRepr([
|
|
0x13ae28e3bc35ebeb,
|
|
0xa10f4488075cae2c,
|
|
0x8160e95a853c3b5d,
|
|
0x5ae3f03b561a841d,
|
|
])));
|
|
assert!(
|
|
tmp == Fr(FrRepr([
|
|
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_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([
|
|
0xffffffffffffffff,
|
|
0xffffffffffffffff,
|
|
0xffffffffffffffff,
|
|
0x73eda753299d7d47,
|
|
]));
|
|
assert!(a.is_valid());
|
|
assert_eq!(
|
|
a.square(),
|
|
Fr::from_repr(FrRepr([
|
|
0xc0d698e7bde077b8,
|
|
0xb79a310579e76ec2,
|
|
0xac1da8d0a9af4e5f,
|
|
0x13f629c49bf23e97
|
|
]))
|
|
.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);
|
|
}
|
|
|
|
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()));
|
|
}
|
|
}
|
|
|
|
#[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,
|
|
]);
|
|
|
|
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_into_repr() {
|
|
// r + 1 should not be in the field
|
|
assert!(Fr::from_repr(FrRepr([
|
|
0xffffffff00000002,
|
|
0x53bda402fffe5bfe,
|
|
0x3339d80809a1d805,
|
|
0x73eda753299d7d48
|
|
]))
|
|
.is_err());
|
|
|
|
// r should not be in the field
|
|
assert!(Fr::from_repr(Fr::char()).is_err());
|
|
|
|
// Multiply some arbitrary representations to see if the result is as expected.
|
|
let a = FrRepr([
|
|
0x25ebe3a3ad3c0c6a,
|
|
0x6990e39d092e817c,
|
|
0x941f900d42f5658e,
|
|
0x44f8a103b38a71e0,
|
|
]);
|
|
let mut a_fr = Fr::from_repr(a).unwrap();
|
|
let b = FrRepr([
|
|
0x264e9454885e2475,
|
|
0x46f7746bb0308370,
|
|
0x4683ef5347411f9,
|
|
0x58838d7f208d4492,
|
|
]);
|
|
let b_fr = Fr::from_repr(b).unwrap();
|
|
let c = FrRepr([
|
|
0x48a09ab93cfc740d,
|
|
0x3a6600fbfc7a671,
|
|
0x838567017501d767,
|
|
0x7161d6da77745512,
|
|
]);
|
|
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());
|
|
|
|
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.into_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_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
|
|
]))
|
|
.unwrap()
|
|
),
|
|
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
|
|
);
|
|
assert_eq!(
|
|
format!(
|
|
"{}",
|
|
Fr::from_repr(FrRepr([
|
|
0x44c71298ff198106,
|
|
0xb0ad10817df79b6a,
|
|
0xd034a80a2b74132b,
|
|
0x41cf9a1336f50719
|
|
]))
|
|
.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() {
|
|
use ff::SqrtField;
|
|
|
|
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::<Fr>();
|
|
crate::tests::field::random_sqrt_tests::<Fr>();
|
|
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
|
crate::tests::field::from_str_tests::<Fr>();
|
|
}
|
|
|
|
#[test]
|
|
fn fr_repr_tests() {
|
|
crate::tests::repr::random_repr_tests::<Fr>();
|
|
}
|