Skip to content

Commit

Permalink
LurkField + refactor quickcheck -> proptest (#240)
Browse files Browse the repository at this point in the history
* refactor: convert quickcheck -> proptest

* nix flake build

* set toolchain to 1.67.0

* Add minimal CODEOWNERS file. (#235)

Co-authored-by: porcuquine <[email protected]>

* Implement coercion to u32 (and to char) (#223)

* Implement coercion to u32 (and to char)

---------

Co-authored-by: porcuquine <[email protected]>

* nix flake build

* Test Rust install on CI

* Another test

* Fix CI

* Fix CI

* refactor LurkField trait

* fix flake

* fix formatting

* fix fcomm

* fix clippy warnings

* remove unnecessary canonical bytes trait methods, add repr canonicity tests

* remove vec_f functions from LurkField, refactor tests

* remove no longer needed store tests

* update FWrap Arbitrary

* Support Wasm by moving proptest to dev-dependencies

* Switch back to simple conditional compilation

---------

Co-authored-by: François Garillot <[email protected]>
Co-authored-by: porcuquine <[email protected]>
Co-authored-by: porcuquine <[email protected]>
Co-authored-by: Eduardo Morais <[email protected]>
Co-authored-by: Samuel Burnham <[email protected]>
  • Loading branch information
6 people authored Feb 8, 2023
1 parent 2bf240a commit 1b55fd2
Show file tree
Hide file tree
Showing 9 changed files with 716 additions and 591 deletions.
429 changes: 293 additions & 136 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ num-traits = "0.2.15"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
memmap = { version = "0.5.8", package = "memmap2" }
pasta-msm = "0.1.1"
proptest = "1.1.0"
proptest-derive = "0.3.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
Expand All @@ -58,8 +60,8 @@ gpu = ["neptune/opencl"]

[dev-dependencies]
criterion = "0.3.5"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
structopt = { version = "0.3", default-features = false }
tap = "1.0.1"
rand = "0.8.4"

[[bench]]
Expand Down
105 changes: 52 additions & 53 deletions src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,45 +224,41 @@ pub mod tests {
use blstrs::Scalar as Fr;

use super::*;
use quickcheck::{Arbitrary, Gen};
use proptest::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;

impl<F: LurkField> Arbitrary for FWrap<F> {
fn arbitrary(_: &mut Gen) -> Self {
let f = F::random(rand::thread_rng());
FWrap(f)
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
let strategy = any::<[u8; 32]>()
.prop_map(|seed| FWrap(F::random(StdRng::from_seed(seed))))
.no_shrink();
strategy.boxed()
}
}

// For working around the orphan trait impl rule
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VecFWrap<F: LurkField>(pub Vec<F>);

impl<F: LurkField> Arbitrary for VecFWrap<F> {
fn arbitrary(g: &mut Gen) -> Self {
let vec_f: Vec<FWrap<F>> = Arbitrary::arbitrary(g);
VecFWrap(vec_f.into_iter().map(|f| f.0).collect())
}
}

fn repr_bytes_consistency<F: LurkField>(f1: FWrap<F>) -> bool {
fn repr_bytes_consistency<F: LurkField>(f1: FWrap<F>) {
let bytes = f1.0.to_repr().as_ref().to_owned();
let f2 = <F as LurkField>::from_bytes(&bytes);
Some(f1.0) == f2
}

#[quickcheck]
fn prop_blstrs_repr_bytes_consistency(f1: FWrap<Fr>) -> bool {
repr_bytes_consistency(f1)
}

#[quickcheck]
fn prop_pallas_repr_bytes_consistency(f1: FWrap<pasta_curves::Fp>) -> bool {
repr_bytes_consistency(f1)
let f2 = <F as LurkField>::from_bytes(&bytes).expect("from_bytes");
assert_eq!(f1.0, f2)
}

#[quickcheck]
fn prop_vesta_repr_bytes_consistency(f1: FWrap<pasta_curves::Fq>) -> bool {
proptest! {
#[test]
fn prop_bls_repr_bytes_consistency(f1 in any::<FWrap<Fr>>()) {
repr_bytes_consistency(f1)
}
#[test]
fn prop_pallas_repr_bytes_consistency(f1 in any::<FWrap<pasta_curves::Fp>>()) {
repr_bytes_consistency(f1)
}
#[test]
fn prop_vesta_repr_bytes_consistency(f1 in any::<FWrap<pasta_curves::Fq>>()) {
repr_bytes_consistency(f1)
}
}

// Construct canonical bytes from a field element
Expand Down Expand Up @@ -300,34 +296,37 @@ pub mod tests {
res
}

fn repr_canonicity<F: LurkField>(f1: FWrap<F>) -> bool {
fn repr_canonicity<F: LurkField>(f1: FWrap<F>) {
let repr_bytes = f1.0.to_bytes();
let canonical_bytes = to_le_bytes_canonical(f1.0);
let f2_repr = F::from_bytes(&repr_bytes).unwrap();
let f2_repr = F::from_bytes(&repr_bytes).expect("from_bytes");
let f2_canonical = from_le_bytes_canonical::<F>(&canonical_bytes);
repr_bytes == canonical_bytes && f2_repr == f2_canonical
assert_eq!(repr_bytes, canonical_bytes);
assert_eq!(f2_repr, f2_canonical)
}

#[quickcheck]
fn prop_blstrs_repr_canonicity(f1: FWrap<Fr>) -> bool {
proptest! {
#[test]
fn prop_repr_canonicity(f1 in any::<FWrap<Fr>>()) {
repr_canonicity(f1)
}

#[quickcheck]
fn prop_pallas_repr_canonicity(f1: FWrap<pasta_curves::Fp>) -> bool {
repr_canonicity(f1)
}

#[quickcheck]
fn prop_vesta_repr_canonicity(f1: FWrap<pasta_curves::Fq>) -> bool {
repr_canonicity(f1)
}

#[quickcheck]
fn prop_tag_consistency(x: ExprTag) -> bool {
let f1 = Fr::from_expr_tag(x);
let tag = <Fr as LurkField>::to_expr_tag(&f1).unwrap();
let f2 = Fr::from_expr_tag(tag);
f1 == f2 && x == tag
}
#[test]
fn prop_pallas_repr_canonicity(f1 in any::<FWrap<pasta_curves::Fp>>()) {
repr_canonicity(f1)
}
#[test]
fn prop_vesta_repr_canonicity(f1 in any::<FWrap<pasta_curves::Fq>>()) {
repr_canonicity(f1)
}
}

proptest! {
fn prop_tag_consistency(x in any::<ExprTag>()) {
let f1 = Fr::from_expr_tag(x);
let tag = <Fr as LurkField>::to_expr_tag(&f1).unwrap();
let f2 = Fr::from_expr_tag(tag);
assert_eq!(f1, f2);
assert_eq!(x, tag)
}
}
}
34 changes: 0 additions & 34 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ extern crate core;
#[macro_use]
extern crate alloc;

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;

pub mod circuit;
pub mod eval;
pub mod field;
Expand All @@ -36,31 +30,3 @@ pub use uint::UInt;
pub const TEST_SEED: [u8; 16] = [
0x62, 0x59, 0x5d, 0xbe, 0x3d, 0x76, 0x3d, 0x8d, 0xdb, 0x17, 0x32, 0x37, 0x06, 0x54, 0xe5, 0xbc,
];

#[cfg(test)]
pub mod test {
use quickcheck::Gen;
use rand::Rng;

// This is a useful testing utility for generating Arbitrary instances of
// enums, by providing generators for each variant, plus a frequency weight
// for how often to choose that variant. It's included in lib::test to make
// it easier to import in the test modules of specific submodules.
pub fn frequency<T, F: Fn(&mut Gen) -> T>(g: &mut Gen, gens: Vec<(i64, F)>) -> T {
if gens.iter().any(|(v, _)| *v < 0) {
panic!("Negative weight");
}
let sum: i64 = gens.iter().map(|x| x.0).sum();
let mut rng = rand::thread_rng();
let mut weight: i64 = rng.gen_range(1..=sum);
// let mut weight: i64 = g.rng.gen_range(1, sum);
for gen in gens {
if weight - gen.0 <= 0 {
return gen.1(g);
} else {
weight -= gen.0;
}
}
panic!("Calculation error for weight = {}", weight);
}
}
43 changes: 18 additions & 25 deletions src/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,40 +262,33 @@ impl<'de, F: LurkField> Deserialize<'de> for Num<F> {
mod tests {
use super::*;

use quickcheck::{Arbitrary, Gen};
use proptest::prelude::*;

use crate::field::FWrap;
use crate::test::frequency;
use blstrs::Scalar;
use blstrs::Scalar as Fr;
use ff::Field;

impl Arbitrary for Num<Fr> {
fn arbitrary(g: &mut Gen) -> Self {
let input: Vec<(i64, Box<dyn Fn(&mut Gen) -> Num<Fr>>)> = vec![
(100, Box::new(|g| Num::U64(Arbitrary::arbitrary(g)))),
(
100,
Box::new(|g| {
let f = FWrap::arbitrary(g);
Num::Scalar(f.0)
}),
),
];
frequency(g, input)
impl<Fr: LurkField> Arbitrary for Num<Fr> {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop_oneof!(
any::<u64>().prop_map(Num::U64),
any::<FWrap<Fr>>().prop_map(|f| Num::Scalar(f.0)),
)
.boxed()
}
}

#[quickcheck]
fn prop_num_ipld(x: Num<Fr>) -> bool {
if let Ok(ipld) = libipld::serde::to_ipld(x) {
if let Ok(y) = libipld::serde::from_ipld(ipld) {
Num::Scalar(x.into_scalar()) == y
} else {
false
}
} else {
false
proptest! {
#[test]
fn prop_num_ipld(x: Num<Fr>) {
let to_ipld = libipld::serde::to_ipld(x).unwrap();
let y = libipld::serde::from_ipld(to_ipld).unwrap();
assert_eq!(
Num::Scalar(x.into_scalar()), y);
}
}

Expand Down
Loading

0 comments on commit 1b55fd2

Please sign in to comment.