From 65bafd607aac4582f453e2d7db8c5b8cbf69727d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Wed, 25 Jan 2023 15:55:14 -0500 Subject: [PATCH 01/21] refactor: convert quickcheck -> proptest --- Cargo.lock | 270 ++++++++++++++++++++++---------- Cargo.toml | 5 +- src/field.rs | 88 ++++++----- src/lib.rs | 34 ---- src/num.rs | 42 ++--- src/scalar_store.rs | 373 ++++++++++++++++++++++---------------------- src/store.rs | 255 +++++++++++++++--------------- src/sym.rs | 3 +- 8 files changed, 567 insertions(+), 503 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b7bf48aa7..d1a491fe6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -169,6 +169,21 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -457,9 +472,9 @@ checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -668,7 +683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" dependencies = [ "data-encoding", - "syn", + "syn 1.0.107", ] [[package]] @@ -797,16 +812,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "log", - "regex", -] - [[package]] name = "errno" version = "0.2.8" @@ -865,8 +870,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5109f6bc9cd57feda665da326f3f6c57e0498c8fe9f7d12d7b8abc96719ca91b" dependencies = [ "execute-command-tokens", - "quote", - "syn", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -875,6 +880,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba569491c70ec8471e34aa7e9c0b9e82bb5d2464c0398442d17d3c4af814e5a" +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "fcomm" version = "0.1.1" @@ -935,9 +949,9 @@ dependencies = [ "num-bigint 0.3.3", "num-integer", "num-traits", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -1133,7 +1147,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error", + "quick-error 1.2.3", ] [[package]] @@ -1161,6 +1175,15 @@ dependencies = [ "rayon", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "io-lifetimes" version = "1.0.4" @@ -1296,8 +1319,8 @@ dependencies = [ "pasta_curves", "peekmore", "pretty_env_logger", - "quickcheck", - "quickcheck_macros", + "proptest", + "proptest-derive", "rand 0.8.5", "rand_xorshift", "rayon", @@ -1307,6 +1330,7 @@ dependencies = [ "serde_repr", "string-interner", "structopt", + "tap", "thiserror", ] @@ -1382,9 +1406,9 @@ version = "0.1.0" dependencies = [ "blstrs", "lurk", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -1479,9 +1503,9 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] @@ -1788,7 +1812,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" dependencies = [ - "env_logger 0.7.1", + "env_logger", "log", ] @@ -1809,9 +1833,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "version_check", ] @@ -1821,11 +1845,20 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.50", + "quote 1.0.23", "version_check", ] +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.50" @@ -1835,6 +1868,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.1", + "rand 0.8.5", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "proptest-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1842,25 +1906,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quickcheck" -version = "1.0.3" +name = "quick-error" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" -dependencies = [ - "env_logger 0.8.4", - "log", - "rand 0.8.5", -] +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] -name = "quickcheck_macros" -version = "1.0.0" +name = "quote" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 0.4.30", ] [[package]] @@ -1869,7 +1926,7 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.50", ] [[package]] @@ -2093,6 +2150,18 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "rustyline" version = "9.1.2" @@ -2123,8 +2192,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688599bdab9f42105d0ae1494335a9ffafdeb7d36325e6b10fd4abc5829d6284" dependencies = [ - "quote", - "syn", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2201,9 +2270,9 @@ version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2223,9 +2292,9 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2345,9 +2414,9 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2356,14 +2425,25 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.50", + "quote 1.0.23", "unicode-ident", ] @@ -2373,10 +2453,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", + "unicode-xid 0.2.4", ] [[package]] @@ -2404,6 +2484,20 @@ dependencies = [ "remove_dir_all", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -2449,9 +2543,9 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2488,9 +2582,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2517,6 +2611,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -2592,9 +2692,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -2604,7 +2704,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote", + "quote 1.0.23", "wasm-bindgen-macro-support", ] @@ -2614,9 +2714,9 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2770,8 +2870,8 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index ab966299ef..e9ec0ca35a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,8 @@ multihash = { version = "0.16.1", default-features = false, features = ["alloc", num-bigint = "0.4.3" num-integer = "0.1.45" num-traits = "0.2.15" +proptest = "1.0.0" +proptest-derive = "0.3.0" [features] default = [] @@ -53,8 +55,7 @@ gpu = ["neptune/opencl"] [dev-dependencies] criterion = "0.3.5" structopt = { version = "0.3", default-features = false } -quickcheck = "1.0.3" -quickcheck_macros = "1.0.0" +tap = "1.0.1" [[bench]] name = "eval" diff --git a/src/field.rs b/src/field.rs index 202bf1849a..00836d4d5d 100644 --- a/src/field.rs +++ b/src/field.rs @@ -214,55 +214,59 @@ mod test { use blstrs::Scalar as Fr; use crate::store::Tag; - use quickcheck::{Arbitrary, Gen}; + use proptest::prelude::*; impl Arbitrary for FWrap { - fn arbitrary(_: &mut Gen) -> Self { - let f = F::random(rand::thread_rng()); - FWrap(f) + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + // This could also be `Just(F::random(rng::thread_rng()))`, but this allows testing `F::NUM` values + let strategy = prop::collection::vec(any::(), F::NUM_BYTES..=F::NUM_BYTES) + .prop_map(|bytes| F::from_bytes(&bytes)) + .prop_filter("values should be canonical", |f| f.is_some()) + .prop_map(|f| FWrap(f.unwrap())); + strategy.boxed() } } - #[quickcheck] - fn test_bytes_consistency(f1: FWrap) -> bool { - let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_bytes(&bytes); - Some(f1.0) == f2 - } + proptest! { + fn test_bytes_consistency(f1 in any::>()) { + let bytes = f1.0.to_repr().as_ref().to_owned(); + let f2 = ::from_bytes(&bytes); + assert_eq!(Some(f1.0), f2) + } + fn test_tag_consistency(x in any::()) { + let f1 = Fr::from(x as u64); + let tag = ::to_tag(f1).unwrap(); + let f2 = Fr::from(tag as u64); + assert_eq!(f1, f2); + assert_eq!(x as u32, tag); + } - #[quickcheck] - fn test_tag_consistency(x: Tag) -> bool { - let f1 = Fr::from(x as u64); - let tag = ::to_tag(f1).unwrap(); - let f2 = Fr::from(tag as u64); - f1 == f2 && x as u32 == tag - } + fn test_multicodec_consistency(x: Tag) { + let f1 = Fr::from(x as u64); + let codec = ::to_multicodec(f1).unwrap(); + let f2 = ::from_multicodec(codec); + println!("x: {x:?}"); + println!("f1: {f1}"); + println!("codec: {codec:0x}"); + println!("f2: {f1}"); + assert_eq!(Some(f1), f2); + } + fn test_multihash_consistency(f1: FWrap) { + let hash = ::to_multihash(f1.0); + let f2 = ::from_multihash(hash); + assert_eq!(Some(f1.0), f2) + } + fn test_cid_consistency(args: (Tag, FWrap)) { + let (tag1, dig1) = args; + let cid = ::to_cid(Fr::from(tag1 as u64), dig1.0).unwrap(); - #[quickcheck] - fn test_multicodec_consistency(x: Tag) -> bool { - let f1 = Fr::from(x as u64); - let codec = ::to_multicodec(f1).unwrap(); - let f2 = ::from_multicodec(codec); - println!("x: {x:?}"); - println!("f1: {f1}"); - println!("codec: {codec:0x}"); - println!("f2: {f1}"); - Some(f1) == f2 - } - #[quickcheck] - fn test_multihash_consistency(f1: FWrap) -> bool { - let hash = ::to_multihash(f1.0); - let f2 = ::from_multihash(hash); - Some(f1.0) == f2 - } - #[quickcheck] - fn test_cid_consistency(args: (Tag, FWrap)) -> bool { - let (tag1, dig1) = args; - let cid = ::to_cid(Fr::from(tag1 as u64), dig1.0).unwrap(); - if let Some((tag2, dig2)) = ::from_cid(cid) { - Fr::from(tag1 as u64) == tag2 && dig1.0 == dig2 - } else { - false + let (tag2, dig2) = ::from_cid(cid).unwrap(); + assert_eq!( + Fr::from(tag1 as u64),tag2); + assert_eq!( dig1.0 , dig2); } } } diff --git a/src/lib.rs b/src/lib.rs index 0b03b74715..e4b43ebc36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,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; @@ -34,31 +28,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>(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); - } -} diff --git a/src/num.rs b/src/num.rs index 3b6cff5d44..5f3f38cf90 100644 --- a/src/num.rs +++ b/src/num.rs @@ -262,40 +262,32 @@ impl<'de, F: LurkField> Deserialize<'de> for Num { 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 { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Num>)> = 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 Arbitrary for Num { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + any::().prop_map(Num::U64), + any::>().prop_map(|f| Num::Scalar(f.0)), + ) + .boxed() } } - #[quickcheck] - fn prop_num_ipld(x: Num) -> 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! { + fn prop_num_ipld(x: Num) { + 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); } } diff --git a/src/scalar_store.rs b/src/scalar_store.rs index 75c70489a5..6e75660a7a 100644 --- a/src/scalar_store.rs +++ b/src/scalar_store.rs @@ -299,228 +299,231 @@ mod test { use crate::{Sym, Symbol}; use blstrs::Scalar as Fr; - use quickcheck::{Arbitrary, Gen}; - - use crate::test::frequency; + use proptest::prelude::*; + use tap::TapFallible; use libipld::serde::from_ipld; use libipld::serde::to_ipld; - impl Arbitrary for ScalarThunk { - fn arbitrary(g: &mut Gen) -> Self { - ScalarThunk { - value: Arbitrary::arbitrary(g), - continuation: Arbitrary::arbitrary(g), - } - } - } + impl Arbitrary for ScalarThunk { + type Parameters = (); + type Strategy = BoxedStrategy; - impl Arbitrary for Symbol { - fn arbitrary(g: &mut Gen) -> Self { - Symbol { - path: Arbitrary::arbitrary(g), - opaque: Arbitrary::arbitrary(g), - } + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (any::>(), any::>()) + .prop_map(|(value, continuation)| Self { + value, + continuation, + }) + .boxed() } } - #[quickcheck] - fn prop_scalar_thunk_ipld(x: ScalarThunk) -> bool { - if let Ok(ipld) = to_ipld(x) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + proptest! { + fn prop_scalar_thunk_ipld(x in any::>()) { + let to_ipld = to_ipld(x).unwrap(); + let y = from_ipld(to_ipld).unwrap(); + assert_eq!(x, y); } } - impl Arbitrary for ScalarExpression { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box ScalarExpression>)> = vec![ - (100, Box::new(|_| Self::Nil)), - ( - 100, - Box::new(|g| Self::Cons(ScalarPtr::arbitrary(g), ScalarPtr::arbitrary(g))), + impl Arbitrary for ScalarExpression { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(Self::Nil), + any::<(ScalarPtr, ScalarPtr)>().prop_map(|(x, y)| Self::Cons(x, y)), + any::().prop_map(|x| Self::Sym(Sym::Sym(x))), + any::().prop_map(|x| Self::Str(x)), + any::>().prop_map(|x| Self::Num(x.0)), + any::<(ScalarPtr, ScalarPtr, ScalarPtr)>().prop_map( + |(arg, body, closed_env)| { + Self::Fun { + arg, + body, + closed_env, + } + } ), - (100, Box::new(|g| Self::Sym(Sym::Sym(Symbol::arbitrary(g))))), - (100, Box::new(|g| Self::Str(String::arbitrary(g)))), - ( - 100, - Box::new(|g| { - let f = FWrap::arbitrary(g); - Self::Num(f.0) - }), - ), - ( - 100, - Box::new(|g| Self::Fun { - arg: ScalarPtr::arbitrary(g), - body: ScalarPtr::arbitrary(g), - closed_env: ScalarPtr::arbitrary(g), - }), - ), - (100, Box::new(|g| Self::Thunk(ScalarThunk::arbitrary(g)))), - ]; - frequency(g, input) + any::>().prop_map(|x| Self::Thunk(x)), + ) + .boxed() } } - impl Arbitrary for ScalarContinuation { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box ScalarContinuation>)> = vec![ - (100, Box::new(|_| Self::Outermost)), - ( - 100, - Box::new(|g| Self::Call { - unevaled_arg: ScalarPtr::arbitrary(g), - saved_env: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), - ), - ( - 100, - Box::new(|g| Self::Call2 { - function: ScalarPtr::arbitrary(g), - saved_env: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), - ), - ( - 100, - Box::new(|g| Self::Tail { - saved_env: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), - ), - (100, Box::new(|_| Self::Error)), - ( - 100, - Box::new(|g| Self::Lookup { - saved_env: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + impl Arbitrary for ScalarContinuation { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(Self::Outermost), + any::<(ScalarPtr, ScalarPtr, ScalarContPtr)>().prop_map( + |(unevaled_arg, saved_env, continuation)| { + Self::Call { + unevaled_arg, + saved_env, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::Unop { - operator: Op1::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + any::<(ScalarPtr, ScalarPtr, ScalarContPtr)>().prop_map( + |(function, saved_env, continuation)| { + Self::Call2 { + function, + saved_env, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::Binop { - operator: Op2::arbitrary(g), - saved_env: ScalarPtr::arbitrary(g), - unevaled_args: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + any::<(ScalarPtr, ScalarContPtr)>().prop_map( + |(saved_env, continuation)| { + Self::Tail { + saved_env, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::Binop2 { - operator: Op2::arbitrary(g), - evaled_arg: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + Just(Self::Error), + any::<(ScalarPtr, ScalarContPtr)>().prop_map( + |(saved_env, continuation)| { + Self::Lookup { + saved_env, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::If { - unevaled_args: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + any::<(Op1, ScalarContPtr)>().prop_map(|(operator, continuation)| { + Self::Unop { + operator, + continuation, + } + }), + any::<(Op2, ScalarPtr, ScalarPtr, ScalarContPtr)>().prop_map( + |(operator, saved_env, unevaled_args, continuation)| { + Self::Binop { + operator, + saved_env, + unevaled_args, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::Let { - var: ScalarPtr::arbitrary(g), - body: ScalarPtr::arbitrary(g), - saved_env: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + any::<(Op2, ScalarPtr, ScalarContPtr)>().prop_map( + |(operator, evaled_arg, continuation)| { + Self::Binop2 { + operator, + evaled_arg, + continuation, + } + } ), - ( - 100, - Box::new(|g| Self::LetRec { - var: ScalarPtr::arbitrary(g), - saved_env: ScalarPtr::arbitrary(g), - body: ScalarPtr::arbitrary(g), - continuation: ScalarContPtr::arbitrary(g), - }), + any::<(ScalarPtr, ScalarContPtr)>().prop_map( + |(unevaled_args, continuation)| { + Self::If { + unevaled_args, + continuation, + } + } ), - (100, Box::new(|_| Self::Dummy)), - (100, Box::new(|_| Self::Terminal)), - ]; - frequency(g, input) + any::<( + ScalarPtr, + ScalarPtr, + ScalarPtr, + ScalarContPtr + )>() + .prop_map(|(var, body, saved_env, continuation)| { + Self::Let { + var, + body, + saved_env, + continuation, + } + }), + any::<( + ScalarPtr, + ScalarPtr, + ScalarPtr, + ScalarContPtr + )>() + .prop_map(|(var, body, saved_env, continuation)| { + Self::LetRec { + var, + body, + saved_env, + continuation, + } + }), + Just(Self::Dummy), + Just(Self::Terminal) + ) + .boxed() } } - #[quickcheck] - fn prop_scalar_expression_ipld(x: ScalarExpression) -> bool { - match to_ipld(x.clone()) { - Ok(ipld) => match from_ipld(ipld.clone()) { - Ok(y) => { - println!("x: {x:?}"); - println!("y: {y:?}"); - x == y - } - Err(e) => { - println!("ser x: {x:?}"); - println!("de ipld: {ipld:?}"); - println!("err e: {e:?}"); - false - } - }, - Err(e) => { + proptest! { + fn prop_scalar_expression_ipld(x: ScalarExpression) { + let to_ipld = to_ipld(x.clone()).tap_err(|e| { println!("ser x: {x:?}"); println!("err e: {e:?}"); - false - } + }).unwrap(); + + let y = from_ipld(to_ipld.clone()).tap_err(|e| { + println!("ser x: {x:?}"); + println!("de ipld: {to_ipld:?}"); + println!("err e: {e:?}"); + }).unwrap(); + + println!("x: {x:?}"); + println!("y: {y:?}"); + + assert_eq!(x, y); + } - } - #[quickcheck] - fn prop_scalar_continuation_ipld(x: ScalarExpression) -> bool { - if let Ok(ipld) = to_ipld(x.clone()) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + fn prop_scalar_continuation_ipld(x: ScalarExpression) { + let to_ipld = to_ipld(x.clone()).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); } + } // This doesn't create well-defined ScalarStores, so is only useful for // testing ipld - impl Arbitrary for ScalarStore { - fn arbitrary(g: &mut Gen) -> Self { - let map: Vec<(ScalarPtr, Option>)> = Arbitrary::arbitrary(g); - let cont_map: Vec<(ScalarContPtr, Option>)> = - Arbitrary::arbitrary(g); - ScalarStore { - scalar_map: map.into_iter().collect(), - scalar_cont_map: cont_map.into_iter().collect(), - pending_scalar_ptrs: Vec::new(), - } + impl Arbitrary for ScalarStore { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + prop::collection::btree_map( + any::>(), + any::>>(), + 0..100, + ), + prop::collection::btree_map( + any::>(), + any::>>(), + 0..100, + ), + ) + .prop_map(|(scalar_map, scalar_cont_map)| ScalarStore { + scalar_map, + scalar_cont_map, + pending_scalar_ptrs: Vec::new(), + }) + .boxed() } } - #[quickcheck] - fn prop_scalar_store_ipld(x: ScalarStore) -> bool { - if let Ok(ipld) = to_ipld(x.clone()) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + proptest! { + fn prop_scalar_store_ipld(x: ScalarStore) { + let to_ipld = to_ipld(x.clone()).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); } } diff --git a/src/store.rs b/src/store.rs index aec43ffcf3..3192686c43 100644 --- a/src/store.rs +++ b/src/store.rs @@ -2712,69 +2712,72 @@ pub mod test { use blstrs::Scalar as Fr; use super::*; - use quickcheck::{Arbitrary, Gen}; - - use crate::test::frequency; + use proptest::prelude::*; use libipld::serde::from_ipld; use libipld::serde::to_ipld; use libipld::Ipld; impl Arbitrary for Tag { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Tag>)> = vec![ - (100, Box::new(|_| Tag::Nil)), - (100, Box::new(|_| Tag::Cons)), - (100, Box::new(|_| Tag::Sym)), - (100, Box::new(|_| Tag::Fun)), - (100, Box::new(|_| Tag::Num)), - (100, Box::new(|_| Tag::Thunk)), - (100, Box::new(|_| Tag::Str)), - ]; - frequency(g, input) + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(Tag::Nil), + Just(Tag::Cons), + Just(Tag::Sym), + Just(Tag::Fun), + Just(Tag::Num), + Just(Tag::Thunk), + Just(Tag::Str), + ) + .boxed() } } + impl Arbitrary for ContTag { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box ContTag>)> = vec![ - (100, Box::new(|_| ContTag::Outermost)), - (100, Box::new(|_| ContTag::Call)), - (100, Box::new(|_| ContTag::Call2)), - (100, Box::new(|_| ContTag::Tail)), - (100, Box::new(|_| ContTag::Error)), - (100, Box::new(|_| ContTag::Lookup)), - (100, Box::new(|_| ContTag::Unop)), - (100, Box::new(|_| ContTag::Binop)), - (100, Box::new(|_| ContTag::Binop2)), - (100, Box::new(|_| ContTag::If)), - (100, Box::new(|_| ContTag::Let)), - (100, Box::new(|_| ContTag::LetRec)), - (100, Box::new(|_| ContTag::Dummy)), - (100, Box::new(|_| ContTag::Terminal)), - (100, Box::new(|_| ContTag::Emit)), - ]; - frequency(g, input) - } - } - - impl Arbitrary for ScalarPtr { - fn arbitrary(g: &mut Gen) -> Self { - let tag = Tag::arbitrary(g); - let val = FWrap::arbitrary(g); - ScalarPtr::from_parts(Fr::from(tag as u64), val.0) - } - } - - #[quickcheck] - fn test_scalar_ptr_ipld(x: ScalarPtr) -> bool { - if let Ok(ipld) = to_ipld(x) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(ContTag::Outermost), + Just(ContTag::Call), + Just(ContTag::Call2), + Just(ContTag::Tail), + Just(ContTag::Error), + Just(ContTag::Lookup), + Just(ContTag::Unop), + Just(ContTag::Binop), + Just(ContTag::Binop2), + Just(ContTag::If), + Just(ContTag::Let), + Just(ContTag::LetRec), + Just(ContTag::Dummy), + Just(ContTag::Terminal), + Just(ContTag::Emit), + ) + .boxed() + } + } + + impl Arbitrary for ScalarPtr { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + any::<(Tag, FWrap)>() + .prop_map(|(tag, val)| ScalarPtr::from_parts(Fr::from(tag as u64), val.0)) + .boxed() + } + } + + proptest! { + fn test_scalar_ptr_ipld(x in any::>()) { + let to_ipld = to_ipld(x).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); } } @@ -2787,25 +2790,24 @@ pub mod test { assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) } - impl Arbitrary for ScalarContPtr { - fn arbitrary(g: &mut Gen) -> Self { - let tag = ContTag::arbitrary(g); - let val = FWrap::arbitrary(g); - ScalarContPtr::from_parts(Fr::from(tag as u64), val.0) + impl Arbitrary for ScalarContPtr { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + any::<(ContTag, FWrap)>() + .prop_map(|(tag, val)| ScalarContPtr::from_parts(Fr::from(tag as u64), val.0)) + .boxed() } } - #[quickcheck] - fn prop_scalar_cont_ptr_ipld(x: ScalarContPtr) -> bool { - if let Ok(ipld) = to_ipld(x) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false - } + proptest! { + fn prop_scalar_cont_ptr_ipld(x in any::>()) { + let to_ipld = to_ipld(x).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); + + } } #[test] @@ -2818,36 +2820,33 @@ pub mod test { } impl Arbitrary for Op1 { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Op1>)> = vec![ - (100, Box::new(|_| Op1::Car)), - (100, Box::new(|_| Op1::Cdr)), - (100, Box::new(|_| Op1::Atom)), - (100, Box::new(|_| Op1::Emit)), - (100, Box::new(|_| Op1::Secret)), - (100, Box::new(|_| Op1::Commit)), - (100, Box::new(|_| Op1::Num)), - (100, Box::new(|_| Op1::Comm)), - (100, Box::new(|_| Op1::Char)), - (100, Box::new(|_| Op1::Eval)), - ]; - frequency(g, input) - } - } - - #[quickcheck] - fn prop_op1_ipld(x: Op1) -> bool { - if let Ok(ipld) = to_ipld(x) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(Op1::Car), + Just(Op1::Cdr), + Just(Op1::Atom), + Just(Op1::Emit), + Just(Op1::Secret), + Just(Op1::Commit), + Just(Op1::Num), + Just(Op1::Comm), + Just(Op1::Char), + Just(Op1::Eval), + ) + .boxed() } } + proptest! { + fn prop_op1_ipld(x in any::()) { + let to_ipld = to_ipld(x).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); + } + } #[test] fn unit_op1_ipld() { assert_eq!( @@ -2857,38 +2856,36 @@ pub mod test { } impl Arbitrary for Op2 { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Op2>)> = vec![ - (100, Box::new(|_| Op2::Sum)), - (100, Box::new(|_| Op2::Diff)), - (100, Box::new(|_| Op2::Product)), - (100, Box::new(|_| Op2::Quotient)), - (100, Box::new(|_| Op2::Equal)), - (100, Box::new(|_| Op2::NumEqual)), - (100, Box::new(|_| Op2::Less)), - (100, Box::new(|_| Op2::Greater)), - (100, Box::new(|_| Op2::LessEqual)), - (100, Box::new(|_| Op2::GreaterEqual)), - (100, Box::new(|_| Op2::Cons)), - (100, Box::new(|_| Op2::StrCons)), - (100, Box::new(|_| Op2::Begin)), - (100, Box::new(|_| Op2::Hide)), - (100, Box::new(|_| Op2::Eval)), - ]; - frequency(g, input) - } - } - - #[quickcheck] - fn prop_op2_ipld_embed(x: Op2) -> bool { - if let Ok(ipld) = to_ipld(x) { - if let Ok(y) = from_ipld(ipld) { - x == y - } else { - false - } - } else { - false + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof!( + Just(Op2::Sum), + Just(Op2::Diff), + Just(Op2::Product), + Just(Op2::Quotient), + Just(Op2::Equal), + Just(Op2::NumEqual), + Just(Op2::Less), + Just(Op2::Greater), + Just(Op2::LessEqual), + Just(Op2::GreaterEqual), + Just(Op2::Cons), + Just(Op2::StrCons), + Just(Op2::Begin), + Just(Op2::Hide), + Just(Op2::Eval) + ) + .boxed() + } + } + + proptest! { + fn prop_op2_ipld(x in any::()) { + let to_ipld = to_ipld(x).unwrap(); + let from_ipld = from_ipld(to_ipld).unwrap(); + assert_eq!(x, from_ipld); } } diff --git a/src/sym.rs b/src/sym.rs index 9f8345ce9f..dbd7e4efd4 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -3,10 +3,11 @@ use crate::parser::{ }; use peekmore::PeekMore; +use proptest_derive::Arbitrary; /// Module for symbol type, Sym. use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash, Arbitrary)] pub struct Symbol { pub path: Vec, // It would be better not to have this here, but it simplifies things in the Store, at least for now. From 37f02e2cc1ae3969139830cb6381562c894c4bc1 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 15:25:44 -0500 Subject: [PATCH 02/21] nix flake build --- .envrc | 6 +++ flake.lock | 102 ++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 46 ++++++++++++++++++++ rust-toolchain | 1 - rust-toolchain.toml | 7 +++ 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..7c7e899581 --- /dev/null +++ b/.envrc @@ -0,0 +1,6 @@ +use_flake() { + watch_file flake.nix + watch_file flake.lock + eval "$(nix print-dev-env)" +} +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..5956c17438 --- /dev/null +++ b/flake.lock @@ -0,0 +1,102 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1675146246, + "narHash": "sha256-upQtcca/sThA5Jkmn5pDaYFoCmPLMyv7bGFCZFcVhqM=", + "owner": "nix-community", + "repo": "fenix", + "rev": "97deb5c86b238c2a000ef4eb92fb40465f086706", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671096816, + "narHash": "sha256-ezQCsNgmpUHdZANDCILm3RvtO1xH8uujk/+EqNvzIOg=", + "owner": "nix-community", + "repo": "naersk", + "rev": "d998160d6a076cfe8f9741e56aeec7e267e3e114", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1675115703, + "narHash": "sha256-4zetAPSyY0D77x+Ww9QBe8RHn1akvIvHJ/kgg8kGDbk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2caf4ef5005ecc68141ecb4aac271079f7371c44", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1675097868, + "narHash": "sha256-BKFLjEzdoFWso7Artln7djf8RbtBynj9wZKIj22LV5g=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "b75803ad31772d105d86f8ebee0cbc8844a4fa29", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..200bf5d45e --- /dev/null +++ b/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + naersk = { + url = "github:nix-community/naersk"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, naersk, fenix }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = (import nixpkgs) { + inherit system; + }; + + toolchain = with fenix.packages.${system}; fromToolchainFile { + file = ./rust-toolchain.toml; # alternatively, dir = ./.; + sha256 = "sha256-/F36bL5WoJ7opVs7o96dwVHE9SEt3am+6N3jPygJRKY="; + + }; + + in rec { + defaultPackage = (naersk.lib.${system}.override { + # For `nix build` & `nix run`: + cargo = toolchain; + rustc = toolchain; + }).buildPackage { + src = ./.; + }; + + # For `nix develop` or `direnv allow`: + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + ocl-icd + toolchain + ]; + }; + } + ); +} diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 77c582d8d9..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.67.0 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..7b013ad8b6 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +channel = "nightly-2023-01-08" +targets = [ "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown" ] +components = [ "rustc", "cargo", "clippy", "rustfmt", "rust-src", +"rust-analysis", "rust-analyzer" ] +profile = "default" + From 0a6d8c7d7d61b0dbae07022c0d335481189df466 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 15:56:06 -0500 Subject: [PATCH 03/21] set toolchain to 1.67.0 --- flake.nix | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 200bf5d45e..c5cc73b3a1 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,7 @@ toolchain = with fenix.packages.${system}; fromToolchainFile { file = ./rust-toolchain.toml; # alternatively, dir = ./.; - sha256 = "sha256-/F36bL5WoJ7opVs7o96dwVHE9SEt3am+6N3jPygJRKY="; + sha256 = "sha256-riZUc+R9V35c/9e8KJUE+8pzpXyl0lRXt3ZkKlxoY0g="; }; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7b013ad8b6..cf84741dd3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2023-01-08" +channel = "1.67.0" targets = [ "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown" ] components = [ "rustc", "cargo", "clippy", "rustfmt", "rust-src", "rust-analysis", "rust-analyzer" ] From 89f3cc9f5960ca3ece21313e1075266492690751 Mon Sep 17 00:00:00 2001 From: porcuquine <1746729+porcuquine@users.noreply.github.com> Date: Sat, 28 Jan 2023 13:40:25 -0800 Subject: [PATCH 04/21] Add minimal CODEOWNERS file. (#235) Co-authored-by: porcuquine --- CODEOWNERS | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..ecebc214f3 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,8 @@ +# Global Owners +* @porcuquine + +# Doc +*.md @jpeg07 + +# Circuit +/src/circuit/* @emmorais From 69658430ac3ac53076910af6ee108558d3aaf594 Mon Sep 17 00:00:00 2001 From: Eduardo Morais Date: Tue, 31 Jan 2023 15:58:09 -0300 Subject: [PATCH 05/21] Implement coercion to u32 (and to char) (#223) * Implement coercion to u32 (and to char) --------- Co-authored-by: porcuquine --- src/circuit/circuit_frame.rs | 293 ++++++++++++++++++----------- src/circuit/gadgets/constraints.rs | 89 +++++++-- src/circuit/gadgets/data.rs | 5 + src/eval.rs | 9 + src/field.rs | 5 + src/proof/nova.rs | 12 ++ 6 files changed, 287 insertions(+), 126 deletions(-) diff --git a/src/circuit/circuit_frame.rs b/src/circuit/circuit_frame.rs index cb62621387..6b475cf15c 100644 --- a/src/circuit/circuit_frame.rs +++ b/src/circuit/circuit_frame.rs @@ -21,7 +21,7 @@ use super::gadgets::constraints::{ self, alloc_equal, alloc_is_zero, enforce_implication, or, pick, sub, }; use crate::circuit::circuit_frame::constraints::{ - add, allocate_is_negative, boolean_to_num, enforce_true, mul, popcount, + add, allocate_is_negative, boolean_to_num, enforce_pack, linear, mul, }; use crate::circuit::gadgets::hashes::{AllocatedConsWitness, AllocatedContWitness}; use crate::circuit::ToInputs; @@ -3008,8 +3008,9 @@ fn apply_continuation>( let num = to_num(result, g); let comm = to_comm(result, g); - let c = to_char(result, g); - let u64_elem = to_u64(&mut cs.namespace(|| "Unop u64"), g, result.hash())?; + + let (u32_elem, u64_elem) = + to_unsigned_integers(&mut cs.namespace(|| "Unop u32 and u64"), g, result.hash())?; let res = multi_case( &mut cs.namespace(|| "Unop case"), @@ -3058,7 +3059,7 @@ fn apply_continuation>( }, CaseClause { key: Op1::Char.as_field(), - value: c.tag(), + value: &g.char_tag, }, CaseClause { key: Op1::Eval.as_field(), @@ -3108,7 +3109,7 @@ fn apply_continuation>( }, CaseClause { key: Op1::Char.as_field(), - value: c.hash(), + value: &u32_elem, }, CaseClause { key: Op1::Eval.as_field(), @@ -4477,16 +4478,6 @@ fn to_comm(x: &AllocatedPtr, g: &GlobalAllocations) -> Alloc AllocatedPtr::from_parts(&g.comm_tag, x.hash()) } -// Return an AllocatedPtr representing a Char whose value is the same as x's. -// -// FIXME: no range-checking is performed, so the result could be an invalid Char. lurk-rs won't actually create such -// proofs because the out-of-range input will lead to an error when evaluating, but malicious input could still lead to -// such a proof. This would violate the principle that Lurk programs over valid input data should always return valid -// output data. -fn to_char(x: &AllocatedPtr, g: &GlobalAllocations) -> AllocatedPtr { - AllocatedPtr::from_parts(&g.char_tag, x.hash()) -} - fn get_named_components>( mut cs: CS, cont_ptr: &AllocatedContPtr, @@ -4639,75 +4630,117 @@ pub fn comparison_helper>( Ok((is_comparison_tag, comp_val, diff_is_negative)) } -// Enforce 0 <= num < 2ˆn. -pub fn enforce_at_most_n_bits>( - mut cs: CS, - g: &GlobalAllocations, - num: AllocatedNum, - n: usize, -) -> Result<(), SynthesisError> { - let num_bits = num.to_bits_le(&mut cs.namespace(|| "u64 remainder bit decomp"))?; - let v = num_bits[n..255].to_vec(); - popcount(&mut cs.namespace(|| "add all MSBs"), &v, &g.false_num); - Ok(()) +// Lurk supported uint coercion +#[derive(Copy, Clone)] +pub enum UnsignedInt { + U32, + U64, } -// Convert from num to u64. -pub fn to_u64>( +impl UnsignedInt { + pub fn num_bits(&self) -> u32 { + match self { + UnsignedInt::U32 => 32, + UnsignedInt::U64 => 64, + } + } +} + +pub fn to_unsigned_integer_helper>( mut cs: CS, g: &GlobalAllocations, - maybe_u64: &AllocatedNum, + field_elem: &AllocatedNum, + field_bn: BigUint, + field_elem_bits: &[Boolean], + size: UnsignedInt, ) -> Result, SynthesisError> { - let p64_bn = BigUint::pow(&BigUint::from_u32(2).unwrap(), 64); - let v = match maybe_u64.get_value() { - Some(v) => v, - None => F::zero(), - }; - let field_bn = BigUint::from_bytes_le(v.to_repr().as_ref()); - - let (q_bn, r_bn) = field_bn.div_rem(&p64_bn); + let power_of_two_bn = BigUint::pow(&BigUint::from_u32(2).unwrap(), size.num_bits()); + let (q_bn, r_bn) = field_bn.div_rem(&power_of_two_bn); let q_num = allocate_unconstrained_bignum(&mut cs.namespace(|| "q"), q_bn)?; let r_num = allocate_unconstrained_bignum(&mut cs.namespace(|| "r"), r_bn)?; + let pow2_size = match size { + UnsignedInt::U32 => &g.power2_32_num, + UnsignedInt::U64 => &g.power2_64_num, + }; - // a = b.q + r - let product = mul( - &mut cs.namespace(|| "product(q,pow(2,64))"), + // field element = pow(2, size).q + r + linear( + &mut cs, + || "product(q,pow(2,size)) + r", &q_num, - &g.power2_64_num, + pow2_size, + &r_num, + field_elem, + ); + + let r_bits = &field_elem_bits[0..size.num_bits() as usize]; + enforce_pack( + &mut cs.namespace(|| "enforce unsigned pack"), + r_bits, + &r_num, )?; - let sum = add(&mut cs.namespace(|| "sum remainder"), &product, &r_num)?; - let u64_decomp = alloc_equal( - &mut cs.namespace(|| "check u64 decomposition"), - &sum, - maybe_u64, + + Ok(r_num) +} + +// Convert from num to unsigned integers by taking the least significant bits. +// The output is a pair of allocated numbers, where the first one corresponds to +// the u32 coercion, while the second corresponds to the u64 coercion. +pub fn to_unsigned_integers>( + mut cs: CS, + g: &GlobalAllocations, + maybe_unsigned: &AllocatedNum, +) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> { + let field_elem = match maybe_unsigned.get_value() { + Some(v) => v, + None => F::zero(), //dummy + }; + let field_bn = BigUint::from_bytes_le(field_elem.to_repr().as_ref()); + // Since bit decomposition is expensive, we compute it only once here + let field_elem_bits = + maybe_unsigned.to_bits_le(&mut cs.namespace(|| "field element bit decomp"))?; + + let r32_num = to_unsigned_integer_helper( + &mut cs.namespace(|| "enforce u32"), + g, + maybe_unsigned, + field_bn.clone(), + &field_elem_bits, + UnsignedInt::U32, )?; - enforce_true( - &mut cs.namespace(|| "enforce u64 decomposition"), - &u64_decomp, + let r64_num = to_unsigned_integer_helper( + &mut cs.namespace(|| "enforce u64"), + g, + maybe_unsigned, + field_bn, + &field_elem_bits, + UnsignedInt::U64, )?; - // enforce remainder range - enforce_at_most_n_bits( - &mut cs.namespace(|| "remainder u64 range"), + Ok((r32_num, r64_num)) +} + +// Convert from num to u64. +pub fn to_u64>( + mut cs: CS, + g: &GlobalAllocations, + maybe_u64: &AllocatedNum, +) -> Result, SynthesisError> { + let field_elem = maybe_u64.get_value().unwrap_or_else(|| F::zero()); // + let field_bn = BigUint::from_bytes_le(field_elem.to_repr().as_ref()); + let field_elem_bits = maybe_u64.to_bits_le(&mut cs.namespace(|| "field element bit decomp"))?; + + let r64_num = to_unsigned_integer_helper( + &mut cs.namespace(|| "enforce u64"), g, - r_num.clone(), - 64, + maybe_u64, + field_bn, + &field_elem_bits, + UnsignedInt::U64, )?; - let output = AllocatedNum::alloc(&mut cs.namespace(|| "u64_op"), || { - Ok(F::from_u64(v.to_u64_unchecked()).unwrap()) - })?; - let u64_is_remainder = alloc_equal( - &mut cs.namespace(|| "check u64 value is the remainder"), - &r_num, - &output, - )?; - enforce_true( - &mut cs.namespace(|| "enforce u64 decomposition output"), - &u64_is_remainder, - )?; - Ok(output) + Ok(r64_num) } // Enforce div and mod operation for U64. We need to show that @@ -5053,7 +5086,7 @@ pub(crate) fn print_cs>(this: &C) -> String { #[cfg(test)] mod tests { use super::*; - use crate::circuit::circuit_frame::constraints::sub; + use crate::circuit::circuit_frame::constraints::{popcount, sub}; use crate::eval::{empty_sym_env, Evaluable, IO}; use crate::proof::Provable; use crate::proof::{groth16::Groth16Prover, Prover}; @@ -5063,6 +5096,7 @@ mod tests { metric_cs::MetricCS, test_cs::TestConstraintSystem, Comparable, Delta, }; use blstrs::{Bls12, Scalar as Fr}; + use ff::{Field, PrimeField}; use pairing_lib::Engine; const DEFAULT_CHUNK_FRAME_COUNT: usize = 1; @@ -5121,9 +5155,9 @@ mod tests { assert!(delta == Delta::Equal); //println!("{}", print_cs(&cs)); - assert_eq!(12513, cs.num_constraints()); + assert_eq!(12494, cs.num_constraints()); assert_eq!(13, cs.num_inputs()); - assert_eq!(12140, cs.aux().len()); + assert_eq!(12125, cs.aux().len()); let public_inputs = multiframe.public_inputs(); let mut rng = rand::thread_rng(); @@ -5505,48 +5539,6 @@ mod tests { assert_eq!(comp_val.hash().get_value(), g.t_ptr.hash().get_value()); } - #[test] - fn test_enforce_n_bits() { - let mut cs = TestConstraintSystem::::new(); - let s = &mut Store::::default(); - - let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); - - let num = crate::Num::::Scalar(Fr::from_u64(42).unwrap()); // 42 = 101010 - let alloc_num = - AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(num.into_scalar())).unwrap(); - - let res = enforce_at_most_n_bits( - &mut cs.namespace(|| "enforce at most n bits"), - &g, - alloc_num, - 6, - ); - assert!(res.is_ok()); - assert!(cs.is_satisfied()); - } - - #[test] - fn test_enforce_n_bits_negative() { - let mut cs = TestConstraintSystem::::new(); - let s = &mut Store::::default(); - - let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); - - let num = crate::Num::::Scalar(Fr::from_u64(42).unwrap()); // 42 = 101010 - let alloc_num = - AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(num.into_scalar())).unwrap(); - - let res = enforce_at_most_n_bits( - &mut cs.namespace(|| "enforce at most n bits"), - &g, - alloc_num, - 5, - ); - assert!(res.is_ok()); - assert!(!cs.is_satisfied()); - } - #[test] fn test_u64_op() { let mut cs = TestConstraintSystem::::new(); @@ -5725,9 +5717,84 @@ mod tests { &mut cs.namespace(|| format!("popcount {x}")), &bits, alloc_popcount.hash(), - ); + ) + .unwrap(); } assert!(cs.is_satisfied()); } + + #[test] + fn test_to_u32() { + let mut cs = TestConstraintSystem::::new(); + let s = &mut Store::::default(); + let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); + + let a = Fr::from_u64(2).unwrap(); + let v = a + Fr::pow_vartime(&Fr::from_u64(2).unwrap(), [32]); + let field_bn = BigUint::from_bytes_le(v.to_repr().as_ref()); + + let a_plus_power2_32_num = + AllocatedNum::alloc(&mut cs.namespace(|| "pow(2, 32) + 2"), || Ok(v)).unwrap(); + + let bits = a_plus_power2_32_num + .to_bits_le(&mut cs.namespace(|| "bits")) + .unwrap(); + + let res = to_unsigned_integer_helper( + &mut cs, + &g, + &a_plus_power2_32_num, + field_bn, + &bits, + UnsignedInt::U32, + ) + .unwrap(); + + assert_eq!(a, res.get_value().unwrap()); + assert!(cs.is_satisfied()); + } + + #[test] + fn test_to_u64() { + let mut cs = TestConstraintSystem::::new(); + let s = &mut Store::::default(); + let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); + + let a = Fr::from_u64(2).unwrap(); + let v = a + Fr::pow_vartime(&Fr::from_u64(2).unwrap(), [64]); + let field_bn = BigUint::from_bytes_le(v.to_repr().as_ref()); + + let a_plus_power2_64_num = + AllocatedNum::alloc(&mut cs.namespace(|| "pow(2, 64) + 2"), || Ok(v)).unwrap(); + + let bits = a_plus_power2_64_num + .to_bits_le(&mut cs.namespace(|| "bits")) + .unwrap(); + + let res = to_unsigned_integer_helper( + &mut cs, + &g, + &a_plus_power2_64_num, + field_bn, + &bits, + UnsignedInt::U64, + ) + .unwrap(); + + assert_eq!(a, res.get_value().unwrap()); + assert!(cs.is_satisfied()); + } + + #[test] + fn test_enforce_pack() { + let mut cs = TestConstraintSystem::::new(); + let a_num = AllocatedNum::alloc(&mut cs.namespace(|| "a num"), || { + Ok(Fr::from_u64(42).unwrap()) + }) + .unwrap(); + let bits = a_num.to_bits_le(&mut cs.namespace(|| "bits")).unwrap(); + enforce_pack(&mut cs, &bits, &a_num).unwrap(); + assert!(cs.is_satisfied()); + } } diff --git a/src/circuit/gadgets/constraints.rs b/src/circuit/gadgets/constraints.rs index 320af8a055..894b02adae 100644 --- a/src/circuit/gadgets/constraints.rs +++ b/src/circuit/gadgets/constraints.rs @@ -77,24 +77,15 @@ pub fn add>( /// is equal to `sum`. /// /// summation(v) = sum +#[allow(dead_code)] pub fn popcount>( cs: &mut CS, v: &Vec, sum: &AllocatedNum, -) { +) -> Result<(), SynthesisError> { let mut v_lc = LinearCombination::::zero(); - for i in 0..v.len() { - match v[i] { - Boolean::Constant(c) => { - if c { - v_lc = v_lc + (F::one(), CS::one()) - } - } - Boolean::Is(ref v) => v_lc = v_lc + (F::one(), v.get_variable()), - Boolean::Not(ref v) => { - v_lc = v_lc + (F::one(), CS::one()) - (F::one(), v.get_variable()) - } - }; + for b in v { + v_lc = add_to_lc::(b, v_lc, F::one())?; } // (summation(v)) * 1 = sum @@ -104,6 +95,53 @@ pub fn popcount>( |lc| lc + CS::one(), |lc| lc + sum.get_variable(), ); + + Ok(()) +} + +pub fn add_to_lc>( + b: &Boolean, + lc: LinearCombination, + scalar: F, +) -> Result, SynthesisError> { + let mut v_lc = lc; + match b { + Boolean::Constant(c) => { + if *c { + v_lc = v_lc + (scalar, CS::one()) + } else { + v_lc = v_lc + (F::zero(), CS::one()) + } + } + Boolean::Is(ref v) => v_lc = v_lc + (scalar, v.get_variable()), + Boolean::Not(ref v) => v_lc = v_lc + (scalar, CS::one()) - (scalar, v.get_variable()), + }; + + Ok(v_lc) +} + +// Enforce v is the bit decomposition of num, therefore we have that 0 <= num < 2ˆ(sizeof(v)). +pub fn enforce_pack>( + mut cs: CS, + v: &[Boolean], + num: &AllocatedNum, +) -> Result<(), SynthesisError> { + let mut coeff = F::one(); + + let mut v_lc = LinearCombination::::zero(); + for b in v { + v_lc = add_to_lc::(b, v_lc, coeff)?; + coeff = coeff.double(); + } + + cs.enforce( + || "pack", + |_| v_lc, + |lc| lc + CS::one(), + |lc| lc + num.get_variable(), + ); + + Ok(()) } /// Adds a constraint to CS, enforcing a difference relationship between the allocated numbers a, b, and difference. @@ -148,6 +186,31 @@ pub fn sub>( Ok(res) } +/// Adds a constraint to CS, enforcing a linear relationship between the +/// allocated numbers a, b, c and num. Namely, the linear equation +/// a * b + c = num is enforced. +/// +/// a * b = num - c +pub fn linear>( + cs: &mut CS, + annotation: A, + a: &AllocatedNum, + b: &AllocatedNum, + c: &AllocatedNum, + num: &AllocatedNum, +) where + A: FnOnce() -> AR, + AR: Into, +{ + // a * b = product + cs.enforce( + annotation, + |lc| lc + a.get_variable(), + |lc| lc + b.get_variable(), + |lc| lc + num.get_variable() - c.get_variable(), + ); +} + /// Adds a constraint to CS, enforcing a product relationship between the allocated numbers a, b, and product. /// /// a * b = product diff --git a/src/circuit/gadgets/data.rs b/src/circuit/gadgets/data.rs index 6197ed703c..d9857de4e9 100644 --- a/src/circuit/gadgets/data.rs +++ b/src/circuit/gadgets/data.rs @@ -117,6 +117,7 @@ pub struct GlobalAllocations { pub true_num: AllocatedNum, pub false_num: AllocatedNum, pub default_num: AllocatedNum, + pub power2_32_num: AllocatedNum, pub power2_64_num: AllocatedNum, } @@ -307,6 +308,9 @@ impl GlobalAllocations { let false_num = allocate_constant(&mut cs.namespace(|| "false"), F::zero())?; let default_num = allocate_constant(&mut cs.namespace(|| "default"), F::zero())?; + let power2_32_ff = F::pow_vartime(&F::from_u64(2).unwrap(), [32]); + let power2_32_num = allocate_constant(&mut cs.namespace(|| "pow(2,32)"), power2_32_ff)?; + let power2_64_ff = F::pow_vartime(&F::from_u64(2).unwrap(), [64]); let power2_64_num = allocate_constant(&mut cs.namespace(|| "pow(2,64)"), power2_64_ff)?; @@ -406,6 +410,7 @@ impl GlobalAllocations { true_num, false_num, default_num, + power2_32_num, power2_64_num, }) } diff --git a/src/eval.rs b/src/eval.rs index 49174e8916..9e4485c3bb 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -3499,6 +3499,15 @@ mod test { test_aux(s, expr, Some(expected_a), None, Some(terminal), None, 2); } + #[test] + fn char_coercion() { + let s = &mut Store::::default(); + let expr = r#"(char (- 0 4294967200))"#; + let expected_a = s.read(r#"#\a"#).unwrap(); + let terminal = s.get_cont_terminal(); + test_aux(s, expr, Some(expected_a), None, Some(terminal), None, 5); + } + #[test] fn commit_num() { let s = &mut Store::::default(); diff --git a/src/field.rs b/src/field.rs index 202bf1849a..bcf4ae1f31 100644 --- a/src/field.rs +++ b/src/field.rs @@ -40,6 +40,11 @@ pub trait LurkField: PrimeField + PrimeFieldBits { byte_array.copy_from_slice(&self.to_repr().as_ref()[0..4]); Some(u32::from_le_bytes(byte_array)) } + fn from_u32(x: u32) -> Option { + let mut bytes = vec![0; 32]; + bytes[0..4].as_mut().copy_from_slice(&x.to_le_bytes()); + Self::from_bytes(&bytes) + } fn to_u64(&self) -> Option { for x in &self.to_repr().as_ref()[8..] { if *x != 0 { diff --git a/src/proof/nova.rs b/src/proof/nova.rs index 55d58c5358..e419c5cba6 100644 --- a/src/proof/nova.rs +++ b/src/proof/nova.rs @@ -2267,6 +2267,18 @@ mod tests { nova_test_aux(s, expr, Some(expected_a), None, Some(terminal), None, 2); } + #[test] + fn outer_prove_char_coercion() { + let s = &mut Store::::default(); + let expr = r#"(char (- 0 4294967200))"#; + let expr2 = r#"(char (- 0 4294967199))"#; + let expected_a = s.read(r#"#\a"#).unwrap(); + let expected_b = s.read(r#"#\b"#).unwrap(); + let terminal = s.get_cont_terminal(); + nova_test_aux(s, expr, Some(expected_a), None, Some(terminal), None, 5); + nova_test_aux(s, expr2, Some(expected_b), None, Some(terminal), None, 5); + } + #[test] fn outer_prove_commit_num() { let s = &mut Store::::default(); From 8882c3e57e3f60611a508c16860a3904d6a98de3 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 15:25:44 -0500 Subject: [PATCH 06/21] nix flake build --- .envrc | 6 +++ flake.lock | 102 ++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 46 ++++++++++++++++++++ rust-toolchain | 1 - rust-toolchain.toml | 7 +++ 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..7c7e899581 --- /dev/null +++ b/.envrc @@ -0,0 +1,6 @@ +use_flake() { + watch_file flake.nix + watch_file flake.lock + eval "$(nix print-dev-env)" +} +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..5956c17438 --- /dev/null +++ b/flake.lock @@ -0,0 +1,102 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1675146246, + "narHash": "sha256-upQtcca/sThA5Jkmn5pDaYFoCmPLMyv7bGFCZFcVhqM=", + "owner": "nix-community", + "repo": "fenix", + "rev": "97deb5c86b238c2a000ef4eb92fb40465f086706", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671096816, + "narHash": "sha256-ezQCsNgmpUHdZANDCILm3RvtO1xH8uujk/+EqNvzIOg=", + "owner": "nix-community", + "repo": "naersk", + "rev": "d998160d6a076cfe8f9741e56aeec7e267e3e114", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1675115703, + "narHash": "sha256-4zetAPSyY0D77x+Ww9QBe8RHn1akvIvHJ/kgg8kGDbk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2caf4ef5005ecc68141ecb4aac271079f7371c44", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1675097868, + "narHash": "sha256-BKFLjEzdoFWso7Artln7djf8RbtBynj9wZKIj22LV5g=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "b75803ad31772d105d86f8ebee0cbc8844a4fa29", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..200bf5d45e --- /dev/null +++ b/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + naersk = { + url = "github:nix-community/naersk"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, naersk, fenix }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = (import nixpkgs) { + inherit system; + }; + + toolchain = with fenix.packages.${system}; fromToolchainFile { + file = ./rust-toolchain.toml; # alternatively, dir = ./.; + sha256 = "sha256-/F36bL5WoJ7opVs7o96dwVHE9SEt3am+6N3jPygJRKY="; + + }; + + in rec { + defaultPackage = (naersk.lib.${system}.override { + # For `nix build` & `nix run`: + cargo = toolchain; + rustc = toolchain; + }).buildPackage { + src = ./.; + }; + + # For `nix develop` or `direnv allow`: + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + ocl-icd + toolchain + ]; + }; + } + ); +} diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 77c582d8d9..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.67.0 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..7b013ad8b6 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +channel = "nightly-2023-01-08" +targets = [ "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown" ] +components = [ "rustc", "cargo", "clippy", "rustfmt", "rust-src", +"rust-analysis", "rust-analyzer" ] +profile = "default" + From 485f34007a34b8d41f526043bf3ea90a49ede08d Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 31 Jan 2023 18:43:46 -0500 Subject: [PATCH 07/21] Test Rust install on CI --- .circleci/config.yml | 35 +++++++++++++++++++---------------- .envrc | 5 ----- flake.nix | 3 +-- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d1cb4b75b2..75e64d9542 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,7 +46,7 @@ commands: steps: - save_cache: name: "Save rustup cache" - key: cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} + key: cargo-v2-{{ checksum "rust-toolchain.toml" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} paths: - "~/.cargo" - "~/.rustup" @@ -55,7 +55,7 @@ commands: steps: - restore_cache: keys: - - cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} + - cargo-v2-{{ checksum "rust-toolchain.toml" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} install_gpu_deps: steps: @@ -71,8 +71,9 @@ jobs: name: Update submodules command: git submodule update --init --recursive - run: curl https://sh.rustup.rs -sSf | sh -s -- -y - - run: rustup install $(cat rust-toolchain) - - run: rustup default $(cat rust-toolchain) + - run: rustup show + #- run: rustup install $(cat rust-toolchain) + #- run: rustup default $(cat rust-toolchain) - run: cargo --version - run: rustc --version - run: @@ -129,12 +130,13 @@ jobs: - attach_workspace: at: "~/" - install_gpu_deps - - run: - name: Install Rust - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - - run: rustup install $(cat rust-toolchain) - - run: rustup default $(cat rust-toolchain) + #- run: + # name: Install Rust + # command: | + # curl https://sh.rustup.rs -sSf | sh -s -- -y + #- run: rustup install $(cat rust-toolchain) + #- run: rustup default $(cat rust-toolchain) + - run: rustup show - run: cargo --version - run: cargo update - run: cargo fetch @@ -153,12 +155,13 @@ jobs: command: git submodule update --init --recursive - attach_workspace: at: "~/" - - run: - name: Install Rust - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - - run: rustup install $(cat rust-toolchain) - - run: rustup default $(cat rust-toolchain) + #- run: + # name: Install Rust + # command: | + # curl https://sh.rustup.rs -sSf | sh -s -- -y + #- run: rustup install $(cat rust-toolchain) + #- run: rustup default $(cat rust-toolchain) + - run: rustup show - run: cargo --version - run: cargo update - run: cargo fetch diff --git a/.envrc b/.envrc index 7c7e899581..3550a30f2d 100644 --- a/.envrc +++ b/.envrc @@ -1,6 +1 @@ -use_flake() { - watch_file flake.nix - watch_file flake.lock - eval "$(nix print-dev-env)" -} use flake diff --git a/flake.nix b/flake.nix index c5cc73b3a1..a739ed47a7 100644 --- a/flake.nix +++ b/flake.nix @@ -22,8 +22,7 @@ toolchain = with fenix.packages.${system}; fromToolchainFile { file = ./rust-toolchain.toml; # alternatively, dir = ./.; sha256 = "sha256-riZUc+R9V35c/9e8KJUE+8pzpXyl0lRXt3ZkKlxoY0g="; - - }; + }; in rec { defaultPackage = (naersk.lib.${system}.override { From b56fe1a017849496fce5862868f77f91388c8225 Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 31 Jan 2023 18:46:25 -0500 Subject: [PATCH 08/21] Another test --- .circleci/config.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 75e64d9542..38a09f1ca9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -130,10 +130,10 @@ jobs: - attach_workspace: at: "~/" - install_gpu_deps - #- run: - # name: Install Rust - # command: | - # curl https://sh.rustup.rs -sSf | sh -s -- -y + - run: + name: Install Rust + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y #- run: rustup install $(cat rust-toolchain) #- run: rustup default $(cat rust-toolchain) - run: rustup show @@ -155,10 +155,10 @@ jobs: command: git submodule update --init --recursive - attach_workspace: at: "~/" - #- run: - # name: Install Rust - # command: | - # curl https://sh.rustup.rs -sSf | sh -s -- -y + - run: + name: Install Rust + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y #- run: rustup install $(cat rust-toolchain) #- run: rustup default $(cat rust-toolchain) - run: rustup show From f7fb687294377537a7618b8023d94f338ec9ffd6 Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 31 Jan 2023 18:49:12 -0500 Subject: [PATCH 09/21] Fix CI --- .circleci/config.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 38a09f1ca9..8c030f3ba7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -69,11 +69,10 @@ jobs: - checkout - run: name: Update submodules - command: git submodule update --init --recursive - - run: curl https://sh.rustup.rs -sSf | sh -s -- -y + command: | + git submodule update --init --recursive + curl https://sh.rustup.rs -sSf | sh -s -- -y - run: rustup show - #- run: rustup install $(cat rust-toolchain) - #- run: rustup default $(cat rust-toolchain) - run: cargo --version - run: rustc --version - run: @@ -134,8 +133,6 @@ jobs: name: Install Rust command: | curl https://sh.rustup.rs -sSf | sh -s -- -y - #- run: rustup install $(cat rust-toolchain) - #- run: rustup default $(cat rust-toolchain) - run: rustup show - run: cargo --version - run: cargo update @@ -159,8 +156,6 @@ jobs: name: Install Rust command: | curl https://sh.rustup.rs -sSf | sh -s -- -y - #- run: rustup install $(cat rust-toolchain) - #- run: rustup default $(cat rust-toolchain) - run: rustup show - run: cargo --version - run: cargo update From 30c8fd43e21b14b6cdcf7c5ea7613e43fbbfe456 Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 31 Jan 2023 18:51:10 -0500 Subject: [PATCH 10/21] Fix CI --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8c030f3ba7..183dda2367 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,10 +152,10 @@ jobs: command: git submodule update --init --recursive - attach_workspace: at: "~/" - - run: - name: Install Rust - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y + - run: + name: Install Rust + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y - run: rustup show - run: cargo --version - run: cargo update From f3ee6a08828c583af83e8cf1390a2fc1bc49b21a Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 19:21:27 -0500 Subject: [PATCH 11/21] refactor LurkField trait --- src/circuit/circuit_frame.rs | 115 +++---- src/circuit/gadgets/data.rs | 29 +- src/eval.rs | 88 +++--- src/field.rs | 336 ++++++++++++-------- src/lib.rs | 57 ++-- src/repl.rs | 9 +- src/scalar_store.rs | 25 +- src/store.rs | 579 ++++++++++------------------------- src/tag.rs | 492 +++++++++++++++++++++++++++++ 9 files changed, 1005 insertions(+), 725 deletions(-) create mode 100644 src/tag.rs diff --git a/src/circuit/circuit_frame.rs b/src/circuit/circuit_frame.rs index 6b475cf15c..5022ae852e 100644 --- a/src/circuit/circuit_frame.rs +++ b/src/circuit/circuit_frame.rs @@ -28,7 +28,8 @@ use crate::circuit::ToInputs; use crate::eval::{Frame, Witness, IO}; use crate::hash_witness::HashWitness; use crate::proof::Provable; -use crate::store::{ContTag, Op1, Op2, Ptr, Store, Tag, Thunk}; +use crate::store::{Ptr, Store, Thunk}; +use crate::tag::{ContTag, ExprTag, Op1, Op2}; use num_bigint::BigUint; use num_integer::Integer; use num_traits::FromPrimitive; @@ -407,7 +408,7 @@ fn add_clause_single<'a, F: LurkField>( impl<'a, F: LurkField> Results<'a, F> { fn add_clauses_expr( &mut self, - key: Tag, + key: ExprTag, result_expr: &'a AllocatedPtr, result_env: &'a AllocatedPtr, result_cont: &'a AllocatedContPtr, @@ -640,14 +641,14 @@ fn reduce_expression>( let mut results = Results::default(); { // Self-evaluating expressions - results.add_clauses_expr(Tag::Nil, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Num, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Fun, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Char, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Str, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Comm, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::Key, expr, env, cont, &g.true_num); - results.add_clauses_expr(Tag::U64, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Nil, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Num, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Fun, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Char, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Str, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Comm, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::Key, expr, env, cont, &g.true_num); + results.add_clauses_expr(ExprTag::U64, expr, env, cont, &g.true_num); }; let cont_is_terminal = alloc_equal( @@ -681,7 +682,7 @@ fn reduce_expression>( implies_equal!(cs, &expr_is_thunk, &expr_thunk_hash, expr.hash()); results.add_clauses_expr( - Tag::Thunk, + ExprTag::Thunk, &expr_thunk_value, env, &expr_thunk_continuation, @@ -712,7 +713,13 @@ fn reduce_expression>( g, )?; - results.add_clauses_expr(Tag::Sym, &sym_result, &sym_env, &sym_cont, &sym_apply_cont); + results.add_clauses_expr( + ExprTag::Sym, + &sym_result, + &sym_env, + &sym_cont, + &sym_apply_cont, + ); // -- // -- @@ -738,7 +745,7 @@ fn reduce_expression>( )?; results.add_clauses_expr( - Tag::Cons, + ExprTag::Cons, &cons_result, &cons_env, &cons_cont, @@ -2918,9 +2925,10 @@ fn apply_continuation>( // IF this is open, we need to know what we are opening. let digest = result.hash(); - let (open_secret_scalar, open_ptr) = match store - .get_maybe_opaque(Tag::Comm, digest.get_value().unwrap_or_else(|| F::zero())) - { + let (open_secret_scalar, open_ptr) = match store.get_maybe_opaque( + ExprTag::Comm, + digest.get_value().unwrap_or_else(|| F::zero()), + ) { Some(commit) => match store.open(commit) { Some((secret, opening)) => (secret, opening), None => (F::zero(), store.get_nil()), // nil is dummy @@ -4766,21 +4774,13 @@ pub fn enforce_u64_div_mod>( (0, 0) // Dummy }; - let alloc_r_num = - AllocatedNum::alloc( - &mut cs.namespace(|| "r num"), - || Ok(F::from_u64(r).unwrap()), - )?; - let alloc_q_num = - AllocatedNum::alloc( - &mut cs.namespace(|| "q num"), - || Ok(F::from_u64(q).unwrap()), - )?; + let alloc_r_num = AllocatedNum::alloc(&mut cs.namespace(|| "r num"), || Ok(F::from_u64(r)))?; + let alloc_q_num = AllocatedNum::alloc(&mut cs.namespace(|| "q num"), || Ok(F::from_u64(q)))?; let alloc_arg1_num = AllocatedNum::alloc(&mut cs.namespace(|| "arg1 num"), || { - Ok(F::from_u64(arg1_u64).unwrap()) + Ok(F::from_u64(arg1_u64)) })?; let alloc_arg2_num = AllocatedNum::alloc(&mut cs.namespace(|| "arg2 num"), || { - Ok(F::from_u64(arg2_u64).unwrap()) + Ok(F::from_u64(arg2_u64)) })?; // a = b * q + r @@ -4857,7 +4857,7 @@ pub fn allocate_unconstrained_bignum>( let bytes_le = bn.to_bytes_le(); let mut bytes_padded = [0u8; 32]; bytes_padded[0..bytes_le.len()].copy_from_slice(&bytes_le); - let ff = F::from_bytes(&bytes_padded).unwrap(); + let ff = F::from_repr_bytes(&bytes_padded).unwrap(); let num = AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(ff)).unwrap(); Ok(num) } @@ -5551,7 +5551,7 @@ mod tests { let a_u64 = to_u64(&mut cs.namespace(|| "u64 op"), &g, alloc_a.hash()).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(a_u64.get_value(), Fr::from_u64(42)); + assert_eq!(a_u64.get_value(), Some(Fr::from_u64(42))); } #[test] @@ -5564,7 +5564,7 @@ mod tests { let a_u64 = to_u64(&mut cs.namespace(|| "u64 op"), &g, alloc_pow2_64.hash()).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(a_u64.get_value(), Fr::from_u64(0)); + assert_eq!(a_u64.get_value(), Some(Fr::from_u64(0))); } #[test] @@ -5577,11 +5577,7 @@ mod tests { }) .unwrap(); let alloc_num = - AllocatedNum::alloc( - &mut cs.namespace(|| "num"), - || Ok(Fr::from_u64(42).unwrap()), - ) - .unwrap(); + AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(Fr::from_u64(42))).unwrap(); let cond = Boolean::Constant(true); let res = enforce_less_than_bound( @@ -5598,15 +5594,9 @@ mod tests { fn test_enforce_less_than_bound() { let mut cs = TestConstraintSystem::::new(); let alloc_num = - AllocatedNum::alloc( - &mut cs.namespace(|| "num"), - || Ok(Fr::from_u64(42).unwrap()), - ) - .unwrap(); - let alloc_bound = AllocatedNum::alloc(&mut cs.namespace(|| "bound"), || { - Ok(Fr::from_u64(43).unwrap()) - }) - .unwrap(); + AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(Fr::from_u64(42))).unwrap(); + let alloc_bound = + AllocatedNum::alloc(&mut cs.namespace(|| "bound"), || Ok(Fr::from_u64(43))).unwrap(); let cond = Boolean::Constant(true); let res = enforce_less_than_bound( @@ -5623,15 +5613,9 @@ mod tests { fn test_enforce_less_than_bound_negative() { let mut cs = TestConstraintSystem::::new(); let alloc_num = - AllocatedNum::alloc( - &mut cs.namespace(|| "num"), - || Ok(Fr::from_u64(43).unwrap()), - ) - .unwrap(); - let alloc_bound = AllocatedNum::alloc(&mut cs.namespace(|| "bound"), || { - Ok(Fr::from_u64(42).unwrap()) - }) - .unwrap(); + AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(Fr::from_u64(43))).unwrap(); + let alloc_bound = + AllocatedNum::alloc(&mut cs.namespace(|| "bound"), || Ok(Fr::from_u64(42))).unwrap(); let cond = Boolean::Constant(true); let res = enforce_less_than_bound( @@ -5664,8 +5648,8 @@ mod tests { ) .unwrap(); assert!(cs.is_satisfied()); - assert_eq!(q.get_value(), Fr::from_u64(8)); - assert_eq!(r.get_value(), Fr::from_u64(2)); + assert_eq!(q.get_value(), Some(Fr::from_u64(8))); + assert_eq!(r.get_value(), Some(Fr::from_u64(2))); } #[test] @@ -5688,8 +5672,8 @@ mod tests { ) .unwrap(); assert!(cs.is_satisfied()); - assert_eq!(q.get_value(), Fr::from_u64(0)); - assert_eq!(r.get_value(), Fr::from_u64(0)); + assert_eq!(q.get_value(), Some(Fr::from_u64(0))); + assert_eq!(r.get_value(), Some(Fr::from_u64(0))); } #[test] @@ -5723,15 +5707,14 @@ mod tests { assert!(cs.is_satisfied()); } - #[test] fn test_to_u32() { let mut cs = TestConstraintSystem::::new(); let s = &mut Store::::default(); let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); - let a = Fr::from_u64(2).unwrap(); - let v = a + Fr::pow_vartime(&Fr::from_u64(2).unwrap(), [32]); + let a = Fr::from_u64(2); + let v = a + Fr::pow_vartime(&Fr::from_u64(2), [32]); let field_bn = BigUint::from_bytes_le(v.to_repr().as_ref()); let a_plus_power2_32_num = @@ -5761,8 +5744,8 @@ mod tests { let s = &mut Store::::default(); let g = GlobalAllocations::new(&mut cs.namespace(|| "global_allocations"), s).unwrap(); - let a = Fr::from_u64(2).unwrap(); - let v = a + Fr::pow_vartime(&Fr::from_u64(2).unwrap(), [64]); + let a = Fr::from_u64(2); + let v = a + Fr::pow_vartime(&Fr::from_u64(2), [64]); let field_bn = BigUint::from_bytes_le(v.to_repr().as_ref()); let a_plus_power2_64_num = @@ -5789,10 +5772,8 @@ mod tests { #[test] fn test_enforce_pack() { let mut cs = TestConstraintSystem::::new(); - let a_num = AllocatedNum::alloc(&mut cs.namespace(|| "a num"), || { - Ok(Fr::from_u64(42).unwrap()) - }) - .unwrap(); + let a_num = + AllocatedNum::alloc(&mut cs.namespace(|| "a num"), || Ok(Fr::from_u64(42))).unwrap(); let bits = a_num.to_bits_le(&mut cs.namespace(|| "bits")).unwrap(); enforce_pack(&mut cs, &bits, &a_num).unwrap(); assert!(cs.is_satisfied()); diff --git a/src/circuit/gadgets/data.rs b/src/circuit/gadgets/data.rs index d9857de4e9..66f61e1e25 100644 --- a/src/circuit/gadgets/data.rs +++ b/src/circuit/gadgets/data.rs @@ -9,9 +9,10 @@ use neptune::{ use super::pointer::AsAllocatedHashComponents; use crate::field::LurkField; -use crate::store::{ContTag, Expression, HashScalar, Op1, Op2, Pointer, Ptr, Store, Tag, Thunk}; +use crate::store::{Expression, HashScalar, Pointer, Ptr, Store, Thunk}; use crate::store::{IntoHashComponents, ScalarPtr}; use crate::store::{ScalarContPtr, ScalarPointer}; +use crate::tag::{ContTag, ExprTag, Op1, Op2}; use super::pointer::{AllocatedContPtr, AllocatedPtr}; @@ -172,15 +173,15 @@ impl GlobalAllocations { &store.get_str("").unwrap(), )?; - let sym_tag = Tag::Sym.allocate_constant(&mut cs.namespace(|| "sym_tag"))?; - let thunk_tag = Tag::Thunk.allocate_constant(&mut cs.namespace(|| "thunk_tag"))?; - let cons_tag = Tag::Cons.allocate_constant(&mut cs.namespace(|| "cons_tag"))?; - let char_tag = Tag::Char.allocate_constant(&mut cs.namespace(|| "char_tag"))?; - let str_tag = Tag::Str.allocate_constant(&mut cs.namespace(|| "str_tag"))?; - let num_tag = Tag::Num.allocate_constant(&mut cs.namespace(|| "num_tag"))?; - let u64_tag = Tag::U64.allocate_constant(&mut cs.namespace(|| "u64_tag"))?; - let comm_tag = Tag::Comm.allocate_constant(&mut cs.namespace(|| "comm_tag"))?; - let fun_tag = Tag::Fun.allocate_constant(&mut cs.namespace(|| "fun_tag"))?; + let sym_tag = ExprTag::Sym.allocate_constant(&mut cs.namespace(|| "sym_tag"))?; + let thunk_tag = ExprTag::Thunk.allocate_constant(&mut cs.namespace(|| "thunk_tag"))?; + let cons_tag = ExprTag::Cons.allocate_constant(&mut cs.namespace(|| "cons_tag"))?; + let char_tag = ExprTag::Char.allocate_constant(&mut cs.namespace(|| "char_tag"))?; + let str_tag = ExprTag::Str.allocate_constant(&mut cs.namespace(|| "str_tag"))?; + let num_tag = ExprTag::Num.allocate_constant(&mut cs.namespace(|| "num_tag"))?; + let u64_tag = ExprTag::U64.allocate_constant(&mut cs.namespace(|| "u64_tag"))?; + let comm_tag = ExprTag::Comm.allocate_constant(&mut cs.namespace(|| "comm_tag"))?; + let fun_tag = ExprTag::Fun.allocate_constant(&mut cs.namespace(|| "fun_tag"))?; let outermost_cont_tag = ContTag::Outermost.allocate_constant(&mut cs.namespace(|| "outermost_cont_tag"))?; @@ -308,10 +309,10 @@ impl GlobalAllocations { let false_num = allocate_constant(&mut cs.namespace(|| "false"), F::zero())?; let default_num = allocate_constant(&mut cs.namespace(|| "default"), F::zero())?; - let power2_32_ff = F::pow_vartime(&F::from_u64(2).unwrap(), [32]); + let power2_32_ff = F::pow_vartime(&F::from_u64(2), [32]); let power2_32_num = allocate_constant(&mut cs.namespace(|| "pow(2,32)"), power2_32_ff)?; - let power2_64_ff = F::pow_vartime(&F::from_u64(2).unwrap(), [64]); + let power2_64_ff = F::pow_vartime(&F::from_u64(2), [64]); let power2_64_num = allocate_constant(&mut cs.namespace(|| "pow(2,64)"), power2_64_ff)?; Ok(Self { @@ -439,7 +440,7 @@ impl Ptr { SynthesisError, > { match maybe_fun.map(|ptr| (ptr, ptr.tag())) { - Some((ptr, Tag::Fun)) => match store.fetch(ptr).expect("missing fun") { + Some((ptr, ExprTag::Fun)) => match store.fetch(ptr).expect("missing fun") { Expression::Fun(arg, body, closed_env) => { let arg = store.get_expr_hash(&arg).expect("missing arg"); let body = store.get_expr_hash(&body).expect("missing body"); @@ -535,7 +536,7 @@ pub fn allocate_constant>( Ok(allocated) } -impl Tag { +impl ExprTag { pub fn allocate_constant>( &self, cs: &mut CS, diff --git a/src/eval.rs b/src/eval.rs index 9e4485c3bb..2de34013b3 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -3,10 +3,8 @@ use crate::field::LurkField; use crate::hash_witness::{ConsName, ConsWitness, ContName, ContWitness}; use crate::num::Num; use crate::store; -use crate::store::{ - ContPtr, ContTag, Continuation, Expression, Op1, Op2, Pointer, Ptr, ScalarPointer, Store, Tag, - Thunk, -}; +use crate::store::{ContPtr, Continuation, Expression, Pointer, Ptr, ScalarPointer, Store, Thunk}; +use crate::tag::{ContTag, ExprTag, Op1, Op2}; use crate::writer::Write; use log::info; use serde::{Deserialize, Serialize}; @@ -181,11 +179,11 @@ impl IO { // Returns any expression that was emitted in this IO (if an output) or previous (if an input). // The intention is that this method will be used to extract and handle all output as needed. pub fn maybe_emitted_expression(&self, store: &Store) -> Option> { - if self.expr.tag() == crate::store::Tag::Thunk - && self.cont.tag() == crate::store::ContTag::Dummy + if self.expr.tag() == crate::tag::ExprTag::Thunk + && self.cont.tag() == crate::tag::ContTag::Dummy { if let Some(Expression::Thunk(thunk)) = store.fetch(&self.expr) { - if thunk.continuation.tag() == crate::store::ContTag::Emit { + if thunk.continuation.tag() == crate::tag::ContTag::Emit { Some(thunk.value) } else { None @@ -412,7 +410,7 @@ fn reduce_with_witness_inner( Control::Return(expr, env, cont) } else { match expr.tag() { - Tag::Thunk => match store + ExprTag::Thunk => match store .fetch(&expr) .ok_or_else(|| store::Error("Fetch failed".into()))? { @@ -422,15 +420,15 @@ fn reduce_with_witness_inner( _ => unreachable!(), }, // Self-evaluating - Tag::Nil - | Tag::Num - | Tag::Fun - | Tag::Char - | Tag::Str - | Tag::Comm - | Tag::U64 - | Tag::Key => Control::ApplyContinuation(expr, env, cont), - Tag::Sym => { + ExprTag::Nil + | ExprTag::Num + | ExprTag::Fun + | ExprTag::Char + | ExprTag::Str + | ExprTag::Comm + | ExprTag::U64 + | ExprTag::Key => Control::ApplyContinuation(expr, env, cont), + ExprTag::Sym => { if expr == store.lurk_sym("nil") || (expr == store.t()) { // NIL and T are self-evaluating symbols, pass them to the continuation in a thunk. @@ -460,7 +458,7 @@ fn reduce_with_witness_inner( .car_cdr_named(ConsName::EnvCar, store, &binding)?; match var_or_rec_binding.tag() { - Tag::Sym => { + ExprTag::Sym => { // We are in a simple env (not a recursive env), // looking at a binding's variable. @@ -510,7 +508,7 @@ fn reduce_with_witness_inner( } } // Start of a recursive_env. - Tag::Cons => { + ExprTag::Cons => { // CIRCUIT: with_cons_binding let rec_env = binding; @@ -528,7 +526,7 @@ fn reduce_with_witness_inner( let val_to_use = { // CIRCUIT: val_to_use match val2.tag() { - Tag::Fun => { + ExprTag::Fun => { closure_to_extend = Some(val2); // CIRCUIT: val2_is_fun @@ -591,7 +589,7 @@ fn reduce_with_witness_inner( } } } - Tag::Cons => { + ExprTag::Cons => { // This should not fail, since expr is a Cons. let (head, rest) = cons_witness.car_cdr_named(ConsName::Expr, store, &expr)?; @@ -1263,7 +1261,7 @@ fn reduce_with_witness_inner( match more_args.tag() { // (fn arg) // Interpreting as call. - Tag::Nil => Control::Return( + ExprTag::Nil => Control::Return( fun_form, env, cont_witness.intern_named_cont( @@ -1381,7 +1379,7 @@ fn apply_continuation( saved_env, continuation, } => match result.tag() { - Tag::Fun => match store + ExprTag::Fun => match store .fetch(&result) .ok_or_else(|| store::Error("Fetch failed".into()))? { @@ -1410,7 +1408,7 @@ fn apply_continuation( _ => unreachable!(), }, ContTag::Call => match result.tag() { - Tag::Fun => match cont_witness + ExprTag::Fun => match cont_witness .fetch_named_cont(ContName::ApplyContinuation, store, &cont) .ok_or_else(|| store::Error("Fetch failed".into()))? { @@ -1449,7 +1447,7 @@ fn apply_continuation( saved_env, continuation, } => match function.tag() { - Tag::Fun => match store + ExprTag::Fun => match store .fetch(&function) .ok_or_else(|| store::Error("Fetch failed".into()))? { @@ -1539,7 +1537,7 @@ fn apply_continuation( } } Op1::Atom => match result.tag() { - Tag::Cons => store.nil(), + ExprTag::Cons => store.nil(), _ => store.t(), }, Op1::Emit => { @@ -1555,16 +1553,16 @@ fn apply_continuation( )); } Op1::Open => match result.tag() { - Tag::Num | Tag::Comm => store.open_mut(result)?.1, + ExprTag::Num | ExprTag::Comm => store.open_mut(result)?.1, _ => return Ok(Control::Error(result, env)), }, Op1::Secret => match result.tag() { - Tag::Num | Tag::Comm => store.secret_mut(result)?, + ExprTag::Num | ExprTag::Comm => store.secret_mut(result)?, _ => return Ok(Control::Error(result, env)), }, Op1::Commit => store.hide(F::zero(), result), Op1::Num => match result.tag() { - Tag::Num | Tag::Comm | Tag::Char | Tag::U64 => { + ExprTag::Num | ExprTag::Comm | ExprTag::Char | ExprTag::U64 => { let scalar_ptr = store .get_expr_hash(&result) .ok_or_else(|| store::Error("expr hash missing".into()))?; @@ -1573,18 +1571,18 @@ fn apply_continuation( _ => return Ok(Control::Error(result, env)), }, Op1::U64 => match result.tag() { - Tag::Num => { + ExprTag::Num => { let scalar_ptr = store .get_expr_hash(&result) .ok_or_else(|| store::Error("expr hash missing".into()))?; store.get_u64(scalar_ptr.value().to_u64_unchecked()) } - Tag::U64 => result, + ExprTag::U64 => result, _ => return Ok(Control::Error(result, env)), }, Op1::Comm => match result.tag() { - Tag::Num | Tag::Comm => { + ExprTag::Num | ExprTag::Comm => { let scalar_ptr = store .get_expr_hash(&result) .ok_or_else(|| store::Error("expr hash missing".into()))?; @@ -1593,7 +1591,7 @@ fn apply_continuation( _ => return Ok(Control::Error(result, env)), }, Op1::Char => match result.tag() { - Tag::Num | Tag::Char => { + ExprTag::Num | ExprTag::Char => { let scalar_ptr = store .get_expr_hash(&result) .ok_or_else(|| store::Error("expr hash missing".into()))?; @@ -1886,7 +1884,7 @@ fn make_thunk( let (result, env, cont) = control.into_results(store); - if let Tag::Thunk = result.tag() { + if let ExprTag::Thunk = result.tag() { unreachable!("make_thunk should never be called with a thunk"); }; @@ -2063,7 +2061,7 @@ fn extend_rec( cons_witness.car_cdr_named(ConsName::EnvCar, store, &binding_or_env)?; match var_or_binding.tag() { // It's a var, so we are extending a simple env with a recursive env. - Tag::Sym | Tag::Nil => { + ExprTag::Sym | ExprTag::Nil => { let cons = cons_witness.cons_named(ConsName::NewRecCadr, store, var, val); let nil = store.nil(); let list = cons_witness.cons_named(ConsName::NewRec, store, cons, nil); @@ -2072,7 +2070,7 @@ fn extend_rec( Ok(res) } // It's a binding, so we are extending a recursive env. - Tag::Cons => { + ExprTag::Cons => { let cons = cons_witness.cons_named(ConsName::NewRecCadr, store, var, val); let cons2 = cons_witness.cons_named(ConsName::NewRec, store, cons, binding_or_env); let res = cons_witness.cons_named(ConsName::ExtendedRec, store, cons2, rest); @@ -2090,7 +2088,7 @@ fn extend_closure( cons_witness: &mut ConsWitness, ) -> Result, ReductionError> { match fun.tag() { - Tag::Fun => match store + ExprTag::Fun => match store .fetch(fun) .ok_or_else(|| store::Error("Fetch failed".into()))? { @@ -2106,7 +2104,7 @@ fn extend_closure( _ => unreachable!(), }, _ => unreachable!( - "fun.tag() stopped being Tag::Fun after already having been checked in caller." + "fun.tag() stopped being ExprTag::Fun after already having been checked in caller." ), } } @@ -2128,10 +2126,10 @@ fn lookup( var: &Ptr, store: &Store, ) -> Result, store::Error> { - assert!(matches!(var.tag(), Tag::Sym)); + assert!(matches!(var.tag(), ExprTag::Sym)); match env.tag() { - Tag::Nil => Ok(store.get_nil()), - Tag::Cons => { + ExprTag::Nil => Ok(store.get_nil()), + ExprTag::Cons => { let (binding, smaller_env) = store.car_cdr(env)?; let (v, val) = store.car_cdr(&binding)?; if v == *var { @@ -2955,7 +2953,7 @@ mod test { .eval() .unwrap(); - assert_eq!(crate::store::Tag::Fun, result_expr.tag()); + assert_eq!(crate::tag::ExprTag::Fun, result_expr.tag()); assert_eq!(3, iterations); } { @@ -4159,7 +4157,7 @@ mod test { let scalar_ptr = &s.get_expr_hash(&x).unwrap(); assert_eq!(&Fr::zero(), scalar_ptr.value()); - assert_eq!(&Tag::Sym.as_field::(), scalar_ptr.tag()); + assert_eq!(&ExprTag::Sym.as_field::(), scalar_ptr.tag()); } #[test] @@ -4212,8 +4210,8 @@ mod test { ); // The tags differ though. - assert_eq!(&Tag::Sym.as_field::(), sym_scalar_ptr.tag()); - assert_eq!(&Tag::Key.as_field::(), key_scalar_ptr.tag()); + assert_eq!(&ExprTag::Sym.as_field::(), sym_scalar_ptr.tag()); + assert_eq!(&ExprTag::Key.as_field::(), key_scalar_ptr.tag()); } #[test] diff --git a/src/field.rs b/src/field.rs index bcf4ae1f31..0535f7f48e 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,9 +1,10 @@ +use std::convert::TryFrom; +use std::hash::Hash; + use ff::{PrimeField, PrimeFieldBits}; -use libipld::cid::Cid; use serde::{Deserialize, Serialize}; -use std::hash::Hash; -use multihash::Multihash; +use crate::tag::{ContTag, ExprTag, Op1, Op2}; pub enum LanguageField { Pallas, @@ -12,24 +13,95 @@ pub enum LanguageField { } pub trait LurkField: PrimeField + PrimeFieldBits { - // These constants are assumed to be based on some global table like - // multicodec, ideally extended to include arbitrary precision codecs - const FIELD_CODEC: u64; - const HASH_CODEC: u64; - const LURK_CODEC_PREFIX: u64 = 0x10de; - const NUM_BYTES: usize; - - fn from_bytes(bs: &[u8]) -> Option { + const FIELD: LanguageField; + + fn from_repr_bytes(bs: &[u8]) -> Option { let mut def: Self::Repr = Self::default().to_repr(); def.as_mut().copy_from_slice(bs); Self::from_repr(def).into() } - // Return a u32 corresponding to the first 4 little-endian bytes of this field element, discarding the remaining bytes. - fn to_u32_unchecked(&self) -> u32 { - let mut byte_array = [0u8; 4]; - byte_array.copy_from_slice(&self.to_repr().as_ref()[0..4]); - u32::from_le_bytes(byte_array) + + // Construct bytes from a field element *ignoring* the trait specific + // implementation of Repr + fn to_le_bytes_canonical(self) -> Vec { + let mut vec = vec![]; + let bits = self.to_le_bits(); + + let len = bits.len(); + let len_bytes = if len % 8 != 0 { len / 8 + 1 } else { len / 8 }; + for _ in 0..len_bytes { + vec.push(0u8) + } + for (n, b) in bits.into_iter().enumerate() { + let (byte_i, bit_i) = (n / 8, n % 8); + if b { + vec[byte_i] += 1u8 << bit_i; + } + } + vec } + + fn hex_digits(self) -> String { + let mut s = String::new(); + let bytes = self.to_le_bytes_canonical(); + for b in bytes.iter().rev() { + s.push_str(&format!("{:02x?}", b)); + } + s + } + + // Construct field element from possibly canonical bytes + fn from_le_bytes_canonical(bs: &[u8]) -> Self { + let mut res = Self::zero(); + let mut bs = bs.iter().rev().peekable(); + while let Some(b) = bs.next() { + let b: Self = (*b as u64).into(); + if bs.peek().is_none() { + res.add_assign(b) + } else { + res.add_assign(b); + res.mul_assign(Self::from(256u64)); + } + } + res + } + + fn to_repr_bytes(self) -> Vec { + let repr = self.to_repr(); + repr.as_ref().to_vec() + } + + fn vec_f_to_bytes(vec_f: Vec) -> Vec { + let mut vec = vec![]; + for f in vec_f { + for byte in f.to_repr_bytes() { + vec.push(byte) + } + } + vec + } + + fn vec_f_from_bytes(vec: &[u8]) -> Option> { + let num_bytes: usize = (Self::NUM_BITS / 8 + 1) as usize; + let mut vec_f: Vec = vec![]; + for chunk in vec.chunks(num_bytes) { + let f: Self = Self::from_repr_bytes(chunk)?; + vec_f.push(f); + } + Some(vec_f) + } + + fn to_u16(&self) -> Option { + for x in &self.to_repr().as_ref()[2..] { + if *x != 0 { + return None; + } + } + let mut byte_array = [0u8; 2]; + byte_array.copy_from_slice(&self.to_repr().as_ref()[0..2]); + Some(u16::from_le_bytes(byte_array)) + } + fn to_u32(&self) -> Option { for x in &self.to_repr().as_ref()[4..] { if *x != 0 { @@ -40,11 +112,12 @@ pub trait LurkField: PrimeField + PrimeFieldBits { byte_array.copy_from_slice(&self.to_repr().as_ref()[0..4]); Some(u32::from_le_bytes(byte_array)) } - fn from_u32(x: u32) -> Option { - let mut bytes = vec![0; 32]; - bytes[0..4].as_mut().copy_from_slice(&x.to_le_bytes()); - Self::from_bytes(&bytes) + + fn to_char(&self) -> Option { + let x = self.to_u32()?; + char::from_u32(x) } + fn to_u64(&self) -> Option { for x in &self.to_repr().as_ref()[8..] { if *x != 0 { @@ -55,16 +128,32 @@ pub trait LurkField: PrimeField + PrimeFieldBits { byte_array.copy_from_slice(&self.to_repr().as_ref()[0..8]); Some(u64::from_le_bytes(byte_array)) } - // Return a u64 corresponding to the first 8 little-endian bytes of this field element, discarding the remaining bytes. + + fn to_u32_unchecked(&self) -> u32 { + let mut byte_array = [0u8; 4]; + byte_array.copy_from_slice(&self.to_repr().as_ref()[0..4]); + u32::from_le_bytes(byte_array) + } + + // Return a u64 corresponding to the first 8 little-endian bytes of this field + // element, discarding the remaining bytes. fn to_u64_unchecked(&self) -> u64 { let mut byte_array = [0u8; 8]; byte_array.copy_from_slice(&self.to_repr().as_ref()[0..8]); u64::from_le_bytes(byte_array) } - fn from_u64(x: u64) -> Option { - let mut bytes = vec![0; 32]; - bytes[0..8].as_mut().copy_from_slice(&x.to_le_bytes()); - Self::from_bytes(&bytes) + + fn from_u64(x: u64) -> Self { + x.into() + } + fn from_u32(x: u32) -> Self { + (x as u64).into() + } + fn from_u16(x: u16) -> Self { + (x as u64).into() + } + fn from_char(x: char) -> Self { + Self::from_u32(x as u32) } fn most_negative() -> Self { @@ -88,100 +177,81 @@ pub trait LurkField: PrimeField + PrimeFieldBits { self.double().is_odd().into() } - // Tags have to be `u32` because we're trying to fit them into `u64` - // multicodecs without overlapping with the existing table. If we can - // implement arbitrary-sized codecs though we can relax this from - // Option, to a arbitrary sized Vec. - fn to_tag(f: Self) -> Option; - - fn to_multicodec(f: Self) -> Option { - let tag: u32 = Self::to_tag(f)?; - - Some(Self::LURK_CODEC_PREFIX << 48 | Self::FIELD_CODEC << 32 | u64::from(tag)) + fn from_expr_tag(tag: ExprTag) -> Self { + Self::from_u64(tag.into()) + } + fn to_expr_tag(&self) -> Option { + let x = Self::to_u16(self)?; + ExprTag::try_from(x).ok() } - fn from_multicodec(codec: u64) -> Option { - let lurk_prefix = (codec & 0xffff_0000_0000_0000) >> 48; - let field_prefix = (codec & 0x0000_ffff_0000_0000) >> 32; - let digest = codec & 0x0000_0000_ffff_ffff; - if lurk_prefix != Self::LURK_CODEC_PREFIX || field_prefix != Self::FIELD_CODEC { - None - } else { - Some(Self::from(digest)) - } + fn from_cont_tag(tag: ContTag) -> Self { + Self::from_u64(tag.into()) } - fn to_multihash(f: Self) -> Multihash { - Multihash::wrap(Self::HASH_CODEC, f.to_repr().as_ref()).unwrap() + fn to_cont_tag(&self) -> Option { + let x = Self::to_u16(self)?; + ContTag::try_from(x).ok() + } + fn from_op1(tag: Op1) -> Self { + Self::from_u64(tag.into()) } - fn from_multihash(hash: Multihash) -> Option { - Self::from_bytes(hash.digest()) + fn to_op1(&self) -> Option { + let x = Self::to_u16(self)?; + Op1::try_from(x).ok() + } + fn from_op2(tag: Op2) -> Self { + Self::from_u64(tag.into()) } - fn to_cid(tag: Self, digest: Self) -> Option { - let codec = Self::to_multicodec(tag)?; - Some(Cid::new_v1(codec, Self::to_multihash(digest))) + fn to_op2(&self) -> Option { + let x = Self::to_u16(self)?; + Op2::try_from(x).ok() } - fn from_cid(cid: Cid) -> Option<(Self, Self)> { - let tag = Self::from_multicodec(cid.codec())?; - let dig = Self::from_multihash(*cid.hash())?; - Some((tag, dig)) + + fn get_field(&self) -> LanguageField { + Self::FIELD } } impl LurkField for blstrs::Scalar { - const FIELD_CODEC: u64 = 1; - const HASH_CODEC: u64 = 2; - const NUM_BYTES: usize = 32; - - fn to_tag(f: Self) -> Option { - let bytes: Vec = f.to_repr().as_ref().to_vec(); - let tag_bytes: [u8; 4] = [bytes[0], bytes[1], bytes[2], bytes[3]]; - if bytes[4..].iter().all(|x| *x == 0) { - Some(u32::from_le_bytes(tag_bytes)) - } else { - None - } - } + const FIELD: LanguageField = LanguageField::BLS12_381; } -impl LurkField for pasta_curves::Fq { - const FIELD_CODEC: u64 = 2; - const HASH_CODEC: u64 = 3; - const NUM_BYTES: usize = 32; - - fn to_tag(f: Self) -> Option { - let bytes: Vec = f.to_repr().as_ref().to_vec(); - let tag_bytes: [u8; 4] = [bytes[0], bytes[1], bytes[2], bytes[3]]; - if bytes[4..].iter().all(|x| *x == 0) { - Some(u32::from_le_bytes(tag_bytes)) - } else { - None - } - } +impl LurkField for pasta_curves::Fp { + const FIELD: LanguageField = LanguageField::Pallas; } -impl LurkField for pasta_curves::Fp { - const FIELD_CODEC: u64 = 3; - const HASH_CODEC: u64 = 3; - const NUM_BYTES: usize = 32; - - fn to_tag(f: Self) -> Option { - let bytes: Vec = f.to_repr().as_ref().to_vec(); - let tag_bytes: [u8; 4] = [bytes[0], bytes[1], bytes[2], bytes[3]]; - if bytes[4..].iter().all(|x| *x == 0) { - Some(u32::from_le_bytes(tag_bytes)) - } else { - None - } - } +impl LurkField for pasta_curves::Fq { + const FIELD: LanguageField = LanguageField::Vesta; } // For working around the orphan trait impl rule #[derive(Clone, Debug, PartialEq, Eq)] pub struct FWrap(pub F); +impl Copy for FWrap {} + +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for FWrap { + fn hash(&self, state: &mut H) { + self.0.to_repr().as_ref().hash(state); + } +} + +impl PartialOrd for FWrap { + fn partial_cmp(&self, other: &Self) -> Option { + (self.0.to_repr().as_ref()).partial_cmp(other.0.to_repr().as_ref()) + } +} + +impl Ord for FWrap { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.0.to_repr().as_ref()).cmp(other.0.to_repr().as_ref()) + } +} + impl Serialize for FWrap { fn serialize(&self, serializer: S) -> Result where @@ -199,26 +269,18 @@ impl<'de, F: LurkField> Deserialize<'de> for FWrap { { use serde::de::Error; let bytes: Vec = Vec::deserialize(deserializer)?; - let f = F::from_bytes(&bytes).ok_or_else(|| { + let f = F::from_repr_bytes(&bytes).ok_or_else(|| { D::Error::custom(format!("expected field element as bytes, got {:?}", &bytes)) })?; Ok(FWrap(f)) } } -#[allow(clippy::derive_hash_xor_eq)] -impl Hash for FWrap { - fn hash(&self, state: &mut H) { - self.0.to_repr().as_ref().hash(state); - } -} - #[cfg(test)] -mod test { - use super::*; +pub mod tests { use blstrs::Scalar as Fr; - use crate::store::Tag; + use super::*; use quickcheck::{Arbitrary, Gen}; impl Arbitrary for FWrap { @@ -228,46 +290,48 @@ mod test { } } + // For working around the orphan trait impl rule + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct VecFWrap(pub Vec); + + impl Arbitrary for VecFWrap { + fn arbitrary(g: &mut Gen) -> Self { + let vec_f: Vec> = Arbitrary::arbitrary(g); + VecFWrap(vec_f.into_iter().map(|f| f.0).collect()) + } + } + #[quickcheck] - fn test_bytes_consistency(f1: FWrap) -> bool { + fn prop_repr_bytes_consistency(f1: FWrap) -> bool { let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_bytes(&bytes); + let f2 = ::from_repr_bytes(&bytes); Some(f1.0) == f2 } #[quickcheck] - fn test_tag_consistency(x: Tag) -> bool { - let f1 = Fr::from(x as u64); - let tag = ::to_tag(f1).unwrap(); - let f2 = Fr::from(tag as u64); - f1 == f2 && x as u32 == tag + fn prop_byte_digits_consistency(f1: FWrap) -> bool { + let bytes = f1.0.to_le_bytes_canonical(); + let f2 = Fr::from_le_bytes_canonical(&bytes); + println!("{:?}", bytes); + println!("f1 0x{}", f1.0.hex_digits()); + println!("f2 0x{}", f2.hex_digits()); + Some(f1.0) == Some(f2) } #[quickcheck] - fn test_multicodec_consistency(x: Tag) -> bool { - let f1 = Fr::from(x as u64); - let codec = ::to_multicodec(f1).unwrap(); - let f2 = ::from_multicodec(codec); - println!("x: {x:?}"); - println!("f1: {f1}"); - println!("codec: {codec:0x}"); - println!("f2: {f1}"); - Some(f1) == f2 - } - #[quickcheck] - fn test_multihash_consistency(f1: FWrap) -> bool { - let hash = ::to_multihash(f1.0); - let f2 = ::from_multihash(hash); - Some(f1.0) == f2 + fn prop_tag_consistency(x: ExprTag) -> bool { + let f1 = Fr::from_expr_tag(x); + let tag = ::to_expr_tag(&f1).unwrap(); + let f2 = Fr::from_expr_tag(tag); + f1 == f2 && x == tag } + #[quickcheck] - fn test_cid_consistency(args: (Tag, FWrap)) -> bool { - let (tag1, dig1) = args; - let cid = ::to_cid(Fr::from(tag1 as u64), dig1.0).unwrap(); - if let Some((tag2, dig2)) = ::from_cid(cid) { - Fr::from(tag1 as u64) == tag2 && dig1.0 == dig2 - } else { - false + fn prop_vec_f_consistency(vec_f: VecFWrap) -> bool { + let bytes = Fr::vec_f_to_bytes(vec_f.0.clone()); + match Fr::vec_f_from_bytes(&bytes) { + Some(vec_f2) => vec_f.0 == vec_f2, + None => false, } } } diff --git a/src/lib.rs b/src/lib.rs index 0b03b74715..980300f9ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,43 +22,52 @@ pub mod repl; pub mod scalar_store; pub mod store; pub mod sym; +pub mod tag; pub mod uint; pub mod writer; mod error; mod num; pub use num::Num; -pub use sym::{Sym, Symbol}; +pub use sym::{ + Sym, + Symbol, +}; 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, + 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; + 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>(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); + // 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>( + 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); + } } diff --git a/src/repl.rs b/src/repl.rs index 8f805d697b..a21e59f78f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -2,7 +2,8 @@ use crate::eval::{empty_sym_env, Evaluator, IO}; use crate::field::LurkField; use crate::package::Package; use crate::parser; -use crate::store::{ContPtr, ContTag, Expression, Pointer, Ptr, Store, Tag}; +use crate::store::{ContPtr, Expression, Pointer, Ptr, Store}; +use crate::tag::{ContTag, ExprTag}; use crate::writer::Write; use anyhow::Result; use peekmore::PeekMore; @@ -186,7 +187,7 @@ impl ReplState { let result = match &maybe_command { Ok(maybe_command) => match maybe_command.tag() { - Tag::Sym => { + ExprTag::Sym => { if let Some(key_string) = store .fetch(maybe_command) .unwrap() @@ -196,7 +197,7 @@ impl ReplState { "QUIT" => (true, false), "LOAD" => match store.read_string(&mut chars) { Ok(s) => match s.tag() { - Tag::Str => { + ExprTag::Str => { let file_path = store.fetch(&s).unwrap(); let file_path = PathBuf::from(file_path.as_str().unwrap()); self.handle_load(store, file_path, package)?; @@ -212,7 +213,7 @@ impl ReplState { }, "RUN" => { if let Ok(s) = store.read_string(&mut chars) { - if s.tag() == Tag::Str { + if s.tag() == ExprTag::Str { let file_path = store.fetch(&s).unwrap(); let file_path = PathBuf::from(file_path.as_str().unwrap()); self.handle_run(store, &file_path, package)?; diff --git a/src/scalar_store.rs b/src/scalar_store.rs index 75c70489a5..0abbf6f6db 100644 --- a/src/scalar_store.rs +++ b/src/scalar_store.rs @@ -2,7 +2,8 @@ use std::collections::BTreeMap; use crate::field::LurkField; -use crate::store::{Op1, Op2, Pointer, Ptr, ScalarContPtr, ScalarPtr, Store, Tag}; +use crate::store::{Pointer, Ptr, ScalarContPtr, ScalarPtr, Store}; +use crate::tag::{ExprTag, Op1, Op2}; use crate::{Num, Sym, UInt}; use serde::Deserialize; use serde::Serialize; @@ -154,22 +155,22 @@ impl ScalarStore { impl ScalarExpression { fn from_ptr(store: &Store, ptr: &Ptr) -> Option { match ptr.tag() { - Tag::Nil => Some(ScalarExpression::Nil), - Tag::Cons => store.fetch_cons(ptr).and_then(|(car, cdr)| { + ExprTag::Nil => Some(ScalarExpression::Nil), + ExprTag::Cons => store.fetch_cons(ptr).and_then(|(car, cdr)| { store.get_expr_hash(car).and_then(|car| { store .get_expr_hash(cdr) .map(|cdr| ScalarExpression::Cons(car, cdr)) }) }), - Tag::Comm => store.fetch_comm(ptr).and_then(|(secret, payload)| { + ExprTag::Comm => store.fetch_comm(ptr).and_then(|(secret, payload)| { store .get_expr_hash(payload) .map(|payload| ScalarExpression::Comm(secret.0, payload)) }), - Tag::Sym => store.fetch_sym(ptr).map(|sym| ScalarExpression::Sym(sym)), - Tag::Key => store.fetch_sym(ptr).map(|sym| ScalarExpression::Sym(sym)), - Tag::Fun => store.fetch_fun(ptr).and_then(|(arg, body, closed_env)| { + ExprTag::Sym => store.fetch_sym(ptr).map(|sym| ScalarExpression::Sym(sym)), + ExprTag::Key => store.fetch_sym(ptr).map(|sym| ScalarExpression::Sym(sym)), + ExprTag::Fun => store.fetch_fun(ptr).and_then(|(arg, body, closed_env)| { store.get_expr_hash(arg).and_then(|arg| { store.get_expr_hash(body).and_then(|body| { store @@ -182,17 +183,17 @@ impl ScalarExpression { }) }) }), - Tag::Num => store.fetch_num(ptr).map(|num| match num { + ExprTag::Num => store.fetch_num(ptr).map(|num| match num { Num::U64(x) => ScalarExpression::Num((*x).into()), Num::Scalar(x) => ScalarExpression::Num(*x), }), - Tag::Str => store + ExprTag::Str => store .fetch_str(ptr) .map(|str| ScalarExpression::Str(str.to_string())), - Tag::Char => store.fetch_char(ptr).map(ScalarExpression::Char), - Tag::U64 => store.fetch_uint(ptr).map(ScalarExpression::UInt), - Tag::Thunk => unimplemented!(), + ExprTag::Char => store.fetch_char(ptr).map(ScalarExpression::Char), + ExprTag::U64 => store.fetch_uint(ptr).map(ScalarExpression::UInt), + ExprTag::Thunk => unimplemented!(), } } } diff --git a/src/store.rs b/src/store.rs index aec43ffcf3..5ea71aa948 100644 --- a/src/store.rs +++ b/src/store.rs @@ -10,18 +10,19 @@ use neptune::poseidon::PoseidonConstants; use once_cell::sync::OnceCell; use libipld::Cid; +use libipld::Multihash; use crate::field::{FWrap, LurkField}; use crate::package::{Package, LURK_EXTERNAL_SYMBOL_NAMES}; use crate::parser::{convert_sym_case, names_keyword}; use crate::scalar_store::{ScalarContinuation, ScalarExpression, ScalarStore}; use crate::sym::Sym; +use crate::tag::{ContTag, ExprTag, Op1, Op2}; use crate::{Num, UInt}; use serde::Deserialize; use serde::Serialize; use serde::{de, ser}; -use serde_repr::{Deserialize_repr, Serialize_repr}; pub enum HashArity { A3, @@ -240,7 +241,7 @@ pub trait ScalarPointer: fmt::Debug + Copy + Clone + PartialEq + H } #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Ptr(Tag, RawPtr); +pub struct Ptr(ExprTag, RawPtr); #[allow(clippy::derive_hash_xor_eq)] impl Hash for Ptr { @@ -252,12 +253,12 @@ impl Hash for Ptr { impl Ptr { pub fn is_nil(&self) -> bool { - matches!(self.0, Tag::Nil) + matches!(self.0, ExprTag::Nil) // FIXME: check value also, probably } pub fn is_fun(&self) -> bool { - matches!(self.0, Tag::Fun) + matches!(self.0, ExprTag::Fun) } pub fn is_opaque(&self) -> bool { @@ -267,15 +268,15 @@ impl Ptr { impl From for Ptr { fn from(c: char) -> Self { - Self(Tag::Char, RawPtr::new(u32::from(c) as usize)) + Self(ExprTag::Char, RawPtr::new(u32::from(c) as usize)) } } impl Pointer for Ptr { - type Tag = Tag; + type Tag = ExprTag; type ScalarPointer = ScalarPtr; - fn tag(&self) -> Tag { + fn tag(&self) -> ExprTag { self.0 } } @@ -305,8 +306,17 @@ impl Serialize for ScalarPtr { S: serde::Serializer, { use ser::Error; - let cid = F::to_cid(self.0, self.1) - .ok_or_else(|| S::Error::custom("expected validly tagged ScalarPtr".to_string()))?; + let tag = self.tag(); + let val = self.value(); + // magic numbers to avoid multicodec table collisons + // this will disappear when we move from IPLD to LDON + let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); + let hash = Multihash::wrap(codec, &val.to_repr_bytes()).or_else(|_| { + Err(S::Error::custom( + "expected validly tagged ScalarPtr".to_string(), + )) + })?; + let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) } } @@ -318,10 +328,10 @@ impl<'de, F: LurkField> Deserialize<'de> for ScalarPtr { { use de::Error; let cid = Cid::deserialize(deserializer)?; - let (tag, dig) = F::from_cid(cid).ok_or_else(|| { - D::Error::custom(format!("expected ScalarPtr encoded as Cid, got {cid}")) - })?; - Ok(ScalarPtr::from_parts(tag, dig)) + let tag = F::from_u64(cid.codec() & 0x0000_0000_ffff_ffff); + let val = F::from_repr_bytes(cid.hash().digest()) + .ok_or_else(|| D::Error::custom("expected ScalarContPtr value".to_string()))?; + Ok(ScalarPtr::from_parts(tag, val)) } } @@ -388,8 +398,17 @@ impl Serialize for ScalarContPtr { S: serde::Serializer, { use ser::Error; - let cid = F::to_cid(self.0, self.1) - .ok_or_else(|| S::Error::custom("expected validly tagged ScalarContPtr".to_string()))?; + let tag = self.tag(); + let val = self.value(); + // magic numbers to avoid multicodec table collisons + // this will disappear when we move from IPLD to LDON + let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); + let hash = Multihash::wrap(codec, &val.to_repr_bytes()).or_else(|_| { + Err(S::Error::custom( + "expected validly tagged ScalarContPtr".to_string(), + )) + })?; + let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) } } @@ -401,10 +420,10 @@ impl<'de, F: LurkField> Deserialize<'de> for ScalarContPtr { { use de::Error; let cid = Cid::deserialize(deserializer)?; - let (tag, dig) = F::from_cid(cid).ok_or_else(|| { - D::Error::custom(format!("expected ScalarContPtr encoded as Cid, got {cid}")) - })?; - Ok(ScalarContPtr::from_parts(tag, dig)) + let tag = F::from_u64(cid.codec() & 0x0000_0000_ffff_ffff); + let val = F::from_repr_bytes(cid.hash().digest()) + .ok_or_else(|| D::Error::custom("expected ScalarContPtr value".to_string()))?; + Ok(ScalarContPtr::from_parts(tag, val)) } } @@ -499,7 +518,7 @@ impl Hash for RawPtr { // 0b 0000_ 0000_0000_0000 // // where typ is -// - `0b0000` for Tag +// - `0b0000` for ExprTag // - `0b0001` for ContTag // - `0b0010` for Op1 // - `0b0011` for Op2 @@ -772,212 +791,6 @@ impl Continuation { } } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u16)] -pub enum Op1 { - Car = 0b0010_0000_0000_0000, - Cdr, - Atom, - Emit, - Open, - Secret, - Commit, - Num, - Comm, - Char, - Eval, - U64, -} - -impl fmt::Display for Op1 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Op1::Car => write!(f, "Car"), - Op1::Cdr => write!(f, "Cdr"), - Op1::Atom => write!(f, "Atom"), - Op1::Emit => write!(f, "Emit"), - Op1::Open => write!(f, "Open"), - Op1::Secret => write!(f, "Secret"), - Op1::Commit => write!(f, "Commit"), - Op1::Num => write!(f, "Num"), - Op1::Comm => write!(f, "Comm"), - Op1::Char => write!(f, "Char"), - Op1::Eval => write!(f, "Eval"), - Op1::U64 => write!(f, "U64"), - } - } -} - -impl Op1 { - pub fn as_field + ff::Field>(&self) -> F { - F::from(*self as u64) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u16)] -pub enum Op2 { - Sum = 0b0011_0000_0000_0000, - Diff, - Product, - Quotient, - Equal, - NumEqual, - Less, - Greater, - LessEqual, - GreaterEqual, - Cons, - StrCons, - Begin, - Hide, - Modulo, - Eval, -} - -impl Op2 { - pub fn as_field + ff::Field>(&self) -> F { - F::from(*self as u64) - } - - pub fn is_numeric(&self) -> bool { - matches!( - self, - Op2::Sum - | Op2::Diff - | Op2::Product - | Op2::Quotient - | Op2::Less - | Op2::Greater - | Op2::LessEqual - | Op2::GreaterEqual - | Op2::NumEqual - | Op2::Modulo - ) - } -} - -impl fmt::Display for Op2 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Op2::Sum => write!(f, "Sum"), - Op2::Diff => write!(f, "Diff"), - Op2::Product => write!(f, "Product"), - Op2::Quotient => write!(f, "Quotient"), - Op2::Equal => write!(f, "Equal"), - Op2::NumEqual => write!(f, "NumEqual"), - Op2::Less => write!(f, "Less"), - Op2::Greater => write!(f, "Greater"), - Op2::LessEqual => write!(f, "LessEqual"), - Op2::GreaterEqual => write!(f, "GreaterEqual"), - Op2::Cons => write!(f, "Cons"), - Op2::StrCons => write!(f, "StrCons"), - Op2::Begin => write!(f, "Begin"), - Op2::Hide => write!(f, "Hide"), - Op2::Modulo => write!(f, "Modulo"), - Op2::Eval => write!(f, "Eval"), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u16)] -pub enum Tag { - Nil = 0b0000_0000_0000_0000, - Cons, - Sym, - Fun, - Num, - Thunk, - Str, - Char, - Comm, - U64, - Key, -} - -impl From for u64 { - fn from(t: Tag) -> Self { - t as u64 - } -} - -impl Tag { - pub fn from_field + ff::Field>(f: F) -> Option { - match f { - f if f == Tag::Nil.as_field() => Some(Tag::Nil), - f if f == Tag::Cons.as_field() => Some(Tag::Cons), - f if f == Tag::Sym.as_field() => Some(Tag::Sym), - f if f == Tag::Key.as_field() => Some(Tag::Key), - f if f == Tag::Fun.as_field() => Some(Tag::Fun), - f if f == Tag::Thunk.as_field() => Some(Tag::Thunk), - f if f == Tag::Num.as_field() => Some(Tag::Num), - f if f == Tag::Str.as_field() => Some(Tag::Str), - f if f == Tag::Char.as_field() => Some(Tag::Char), - f if f == Tag::Comm.as_field() => Some(Tag::Comm), - _ => None, - } - } - - pub fn as_field + ff::Field>(&self) -> F { - F::from(*self as u64) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[repr(u16)] -pub enum ContTag { - Outermost = 0b0001_0000_0000_0000, - Call0, - Call, - Call2, - Tail, - Error, - Lookup, - Unop, - Binop, - Binop2, - If, - Let, - LetRec, - Dummy, - Terminal, - Emit, -} - -impl From for u64 { - fn from(t: ContTag) -> Self { - t as u64 - } -} - -impl ContTag { - pub fn from_field + ff::Field>(f: F) -> Option { - match f { - f if f == ContTag::Outermost.as_field() => Some(ContTag::Outermost), - f if f == ContTag::Call0.as_field() => Some(ContTag::Call0), - f if f == ContTag::Call.as_field() => Some(ContTag::Call), - f if f == ContTag::Call2.as_field() => Some(ContTag::Call2), - f if f == ContTag::Tail.as_field() => Some(ContTag::Tail), - f if f == ContTag::Error.as_field() => Some(ContTag::Error), - f if f == ContTag::Lookup.as_field() => Some(ContTag::Lookup), - f if f == ContTag::Unop.as_field() => Some(ContTag::Unop), - f if f == ContTag::Binop.as_field() => Some(ContTag::Binop), - f if f == ContTag::Binop2.as_field() => Some(ContTag::Binop2), - f if f == ContTag::If.as_field() => Some(ContTag::If), - f if f == ContTag::Let.as_field() => Some(ContTag::Let), - f if f == ContTag::LetRec.as_field() => Some(ContTag::LetRec), - f if f == ContTag::Dummy.as_field() => Some(ContTag::Dummy), - f if f == ContTag::Terminal.as_field() => Some(ContTag::Terminal), - f if f == ContTag::Emit.as_field() => Some(ContTag::Emit), - _ => None, - } - } - pub fn as_field + ff::Field>(&self) -> F { - F::from(*self as u64) - } -} - impl Default for Store { fn default() -> Self { let mut store = Store { @@ -1058,7 +871,7 @@ impl Store { pub fn hidden(&self, secret: F, payload: Ptr) -> Option> { self.comm_store .get_index_of(&(FWrap(secret), payload)) - .map(|c| Ptr(Tag::Comm, RawPtr::new(c))) + .map(|c| Ptr(ExprTag::Comm, RawPtr::new(c))) } pub fn hide(&mut self, secret: F, payload: Ptr) -> Ptr { @@ -1067,10 +880,10 @@ impl Store { pub fn open(&self, ptr: Ptr) -> Option<(F, Ptr)> { let p = match ptr.0 { - Tag::Comm => ptr, - Tag::Num => { + ExprTag::Comm => ptr, + ExprTag::Num => { let scalar = self.fetch_num(&ptr).map(|x| x.into_scalar()).unwrap(); - match self.get_maybe_opaque(Tag::Comm, scalar) { + match self.get_maybe_opaque(ExprTag::Comm, scalar) { Some(c) => c, None => { panic!("Can't find commitment in the store.") @@ -1089,8 +902,8 @@ impl Store { pub fn open_mut(&mut self, ptr: Ptr) -> Result<(F, Ptr), Error> { let p = match ptr.0 { - Tag::Comm => ptr, - Tag::Num => { + ExprTag::Comm => ptr, + ExprTag::Num => { let scalar = self.fetch_num(&ptr).map(|x| x.into_scalar()).unwrap(); self.intern_maybe_opaque_comm(scalar) @@ -1107,7 +920,7 @@ impl Store { pub fn secret(&self, ptr: Ptr) -> Option> { let p = match ptr.0 { - Tag::Comm => ptr, + ExprTag::Comm => ptr, _ => return None, }; @@ -1117,7 +930,7 @@ impl Store { pub fn secret_mut(&mut self, ptr: Ptr) -> Result, Error> { let p = match ptr.0 { - Tag::Comm => ptr, + ExprTag::Comm => ptr, _ => return Err(Error("wrong type for commitment specifier".into())), }; @@ -1217,7 +1030,7 @@ impl Store { } let (p, inserted) = self.cons_store.insert_full((car, cdr)); - let ptr = Ptr(Tag::Cons, RawPtr::new(p)); + let ptr = Ptr(ExprTag::Cons, RawPtr::new(p)); if inserted { self.dehydrated.push(ptr); } @@ -1229,7 +1042,7 @@ impl Store { self.hash_expr(&car); self.hash_expr(&cdr); } - assert_eq!((car.tag(), cdr.tag()), (Tag::Char, Tag::Str)); + assert_eq!((car.tag(), cdr.tag()), (ExprTag::Char, ExprTag::Str)); let (c, s) = ( self.fetch_char(&car).unwrap(), self.fetch_str(&cdr).unwrap(), @@ -1245,7 +1058,7 @@ impl Store { } let (p, inserted) = self.comm_store.insert_full((FWrap(secret), payload)); - let ptr = Ptr(Tag::Comm, RawPtr::new(p)); + let ptr = Ptr(ExprTag::Comm, RawPtr::new(p)); if inserted { self.dehydrated.push(ptr); } @@ -1254,17 +1067,17 @@ impl Store { // Intern a potentially-opaque value. If the corresponding value is already known to the store, // return the known value. - fn intern_maybe_opaque(&mut self, tag: Tag, hash: F) -> Ptr { + fn intern_maybe_opaque(&mut self, tag: ExprTag, hash: F) -> Ptr { self.intern_opaque_aux(tag, hash, true) } // Intern an opaque value. If the corresponding non-opaque value is already known to the store, // return an opaque one anyway. - fn intern_opaque(&mut self, tag: Tag, hash: F) -> Ptr { + fn intern_opaque(&mut self, tag: ExprTag, hash: F) -> Ptr { self.intern_opaque_aux(tag, hash, false) } - pub fn get_maybe_opaque(&self, tag: Tag, hash: F) -> Option> { + pub fn get_maybe_opaque(&self, tag: ExprTag, hash: F) -> Option> { let scalar_ptr = ScalarPtr::from_parts(tag.as_field(), hash); let ptr = self.scalar_ptr_map.get(&scalar_ptr); @@ -1278,7 +1091,7 @@ impl Store { // `return_non_opaque_if_existing` is true, return the known value. fn intern_opaque_aux( &mut self, - tag: Tag, + tag: ExprTag, hash: F, return_non_opaque_if_existing: bool, ) -> Ptr { @@ -1307,7 +1120,7 @@ impl Store { scalar_store: &ScalarStore, ) -> Option> { use ScalarContinuation::*; - let tag: ContTag = ContTag::from_field(*ptr.tag())?; + let tag: ContTag = ContTag::from_field(ptr.tag())?; if let Some(cont) = scalar_store.get_cont(&ptr) { let continuation = match cont { @@ -1424,21 +1237,21 @@ impl Store { ptr: ScalarPtr, scalar_store: &ScalarStore, ) -> Option> { - let tag: Tag = Tag::from_field(*ptr.tag())?; + let tag: ExprTag = ExprTag::from_field(ptr.tag())?; let expr = scalar_store.get_expr(&ptr); use ScalarExpression::*; match (tag, expr) { - (Tag::Nil, Some(Nil)) => Some(self.intern_nil()), - (Tag::Cons, Some(Cons(car, cdr))) => { + (ExprTag::Nil, Some(Nil)) => Some(self.intern_nil()), + (ExprTag::Cons, Some(Cons(car, cdr))) => { let car = self.intern_scalar_ptr(*car, scalar_store)?; let cdr = self.intern_scalar_ptr(*cdr, scalar_store)?; Some(self.intern_cons(car, cdr)) } - (Tag::Str, Some(Str(s))) => Some(self.intern_str(s)), - (Tag::Sym, Some(Sym(s))) => Some(self.intern_sym(s)), - (Tag::Key, Some(Sym(_))) => todo!(), - (Tag::Num, Some(Num(x))) => Some(self.intern_num(crate::Num::Scalar(*x))), - (Tag::Thunk, Some(Thunk(t))) => { + (ExprTag::Str, Some(Str(s))) => Some(self.intern_str(s)), + (ExprTag::Sym, Some(Sym(s))) => Some(self.intern_sym(s)), + (ExprTag::Key, Some(Sym(_))) => todo!(), + (ExprTag::Num, Some(Num(x))) => Some(self.intern_num(crate::Num::Scalar(*x))), + (ExprTag::Thunk, Some(Thunk(t))) => { let value = self.intern_scalar_ptr(t.value, scalar_store)?; let continuation = self.intern_scalar_cont_ptr(t.continuation, scalar_store)?; Some(self.intern_thunk(crate::store::Thunk { @@ -1447,7 +1260,7 @@ impl Store { })) } ( - Tag::Fun, + ExprTag::Fun, Some(Fun { arg, body, @@ -1465,35 +1278,35 @@ impl Store { } pub fn intern_maybe_opaque_fun(&mut self, hash: F) -> Ptr { - self.intern_maybe_opaque(Tag::Fun, hash) + self.intern_maybe_opaque(ExprTag::Fun, hash) } pub fn intern_maybe_opaque_sym(&mut self, hash: F) -> Ptr { - self.intern_maybe_opaque(Tag::Sym, hash) + self.intern_maybe_opaque(ExprTag::Sym, hash) } pub fn intern_maybe_opaque_cons(&mut self, hash: F) -> Ptr { - self.intern_maybe_opaque(Tag::Cons, hash) + self.intern_maybe_opaque(ExprTag::Cons, hash) } pub fn intern_maybe_opaque_comm(&mut self, hash: F) -> Ptr { - self.intern_maybe_opaque(Tag::Comm, hash) + self.intern_maybe_opaque(ExprTag::Comm, hash) } pub fn intern_opaque_fun(&mut self, hash: F) -> Ptr { - self.intern_opaque(Tag::Fun, hash) + self.intern_opaque(ExprTag::Fun, hash) } pub fn intern_opaque_sym(&mut self, hash: F) -> Ptr { - self.intern_opaque(Tag::Sym, hash) + self.intern_opaque(ExprTag::Sym, hash) } pub fn intern_opaque_cons(&mut self, hash: F) -> Ptr { - self.intern_opaque(Tag::Cons, hash) + self.intern_opaque(ExprTag::Cons, hash) } pub fn intern_opaque_comm(&mut self, hash: F) -> Ptr { - self.intern_opaque(Tag::Comm, hash) + self.intern_opaque(ExprTag::Comm, hash) } /// Helper to allocate a list, instead of manually using `cons`. @@ -1524,14 +1337,14 @@ impl Store { let name = name.as_ref(); let (tag, symbol_name) = if name == ".LURK.NIL" { - (Tag::Nil, "LURK.NIL") + (ExprTag::Nil, "LURK.NIL") } else { let (names_keyword, symbol_name) = names_keyword(name); if names_keyword { - (Tag::Key, symbol_name) + (ExprTag::Key, symbol_name) } else { - (Tag::Sym, symbol_name) + (ExprTag::Sym, symbol_name) } }; @@ -1548,14 +1361,14 @@ impl Store { self.hash_string_mut(name); let (tag, symbol_name) = if name == ".LURK.NIL" { - (Tag::Nil, "LURK.NIL") + (ExprTag::Nil, "LURK.NIL") } else { let (names_keyword, symbol_name) = names_keyword(name); if names_keyword { - (Tag::Key, symbol_name) + (ExprTag::Key, symbol_name) } else { - (Tag::Sym, symbol_name) + (ExprTag::Sym, symbol_name) } }; @@ -1598,7 +1411,7 @@ impl Store { }; let (ptr, _) = self.num_store.insert_full(num); - Ptr(Tag::Num, RawPtr::new(ptr)) + Ptr(ExprTag::Num, RawPtr::new(ptr)) } pub fn get_num>>(&self, num: T) -> Option> { @@ -1616,7 +1429,7 @@ impl Store { self.num_store .get_index_of::>(&num) - .map(|x| Ptr(Tag::Num, RawPtr::new(x))) + .map(|x| Ptr(ExprTag::Num, RawPtr::new(x))) } pub fn get_char(&self, c: char) -> Ptr { @@ -1624,11 +1437,11 @@ impl Store { } pub fn get_char_from_u32(&self, code: u32) -> Ptr { - Ptr(Tag::Char, RawPtr::new(code as usize)) + Ptr(ExprTag::Char, RawPtr::new(code as usize)) } pub fn get_u64(&self, n: u64) -> Ptr { - Ptr(Tag::U64, RawPtr::new(n as usize)) + Ptr(ExprTag::U64, RawPtr::new(n as usize)) } pub fn intern_str>(&mut self, str: T) -> Ptr { @@ -1639,10 +1452,10 @@ impl Store { fn intern_str_aux>(&mut self, str: T) -> Ptr { if let Some(ptr) = self.str_store.0.get(&str) { - Ptr(Tag::Str, RawPtr::new(ptr.to_usize())) + Ptr(ExprTag::Str, RawPtr::new(ptr.to_usize())) } else { let ptr = self.str_store.0.get_or_intern(str); - let ptr = Ptr(Tag::Str, RawPtr::new(ptr.to_usize())); + let ptr = Ptr(ExprTag::Str, RawPtr::new(ptr.to_usize())); self.dehydrated.push(ptr); ptr @@ -1651,20 +1464,20 @@ impl Store { pub fn get_str>(&self, name: T) -> Option> { let ptr = self.str_store.0.get(name)?; - Some(Ptr(Tag::Str, RawPtr::new(ptr.to_usize()))) + Some(Ptr(ExprTag::Str, RawPtr::new(ptr.to_usize()))) } pub fn get_sym>(&self, sym: Sym) -> Option> { let name = sym.full_sym_name(); let ptr = self.sym_store.0.get(name)?; - Some(Ptr(Tag::Sym, RawPtr::new(ptr.to_usize()))) + Some(Ptr(ExprTag::Sym, RawPtr::new(ptr.to_usize()))) } pub fn intern_fun(&mut self, arg: Ptr, body: Ptr, closed_env: Ptr) -> Ptr { // TODO: closed_env must be an env - assert!(matches!(arg.0, Tag::Sym), "ARG must be a symbol"); + assert!(matches!(arg.0, ExprTag::Sym), "ARG must be a symbol"); let (p, inserted) = self.fun_store.insert_full((arg, body, closed_env)); - let ptr = Ptr(Tag::Fun, RawPtr::new(p)); + let ptr = Ptr(ExprTag::Fun, RawPtr::new(p)); if inserted { self.dehydrated.push(ptr); } @@ -1673,7 +1486,7 @@ impl Store { pub fn intern_thunk(&mut self, thunk: Thunk) -> Ptr { let (p, inserted) = self.thunk_store.insert_full(thunk); - let ptr = Ptr(Tag::Thunk, RawPtr::new(p)); + let ptr = Ptr(ExprTag::Thunk, RawPtr::new(p)); if inserted { self.dehydrated.push(ptr); } @@ -1745,40 +1558,40 @@ impl Store { } pub(crate) fn fetch_sym(&self, ptr: &Ptr) -> Option { - debug_assert!(matches!(ptr.0, Tag::Sym | Tag::Key | Tag::Nil)); + debug_assert!(matches!(ptr.0, ExprTag::Sym | ExprTag::Key | ExprTag::Nil)); if ptr.1.is_opaque() { - let is_keyword = ptr.0 == Tag::Key; + let is_keyword = ptr.0 == ExprTag::Key; return Some(Sym::new_opaque(is_keyword)); } - if ptr.0 == Tag::Nil { + if ptr.0 == ExprTag::Nil { return Some(Sym::new(".LURK.NIL".into())); }; self.sym_store .0 .resolve(SymbolUsize::try_from_usize(ptr.1.idx()).unwrap()) .map(|s| match ptr.0 { - Tag::Sym => Sym::new_sym(s.into()), - Tag::Key => Sym::new_key(s.into()), + ExprTag::Sym => Sym::new_sym(s.into()), + ExprTag::Key => Sym::new_key(s.into()), _ => unreachable!(), }) } pub(crate) fn fetch_str(&self, ptr: &Ptr) -> Option<&str> { - debug_assert!(matches!(ptr.0, Tag::Str)); + debug_assert!(matches!(ptr.0, ExprTag::Str)); let symbol = SymbolUsize::try_from_usize(ptr.1.idx()).expect("invalid pointer"); self.str_store.0.resolve(symbol) } pub(crate) fn fetch_char(&self, ptr: &Ptr) -> Option { - debug_assert!(matches!(ptr.0, Tag::Char)); + debug_assert!(matches!(ptr.0, ExprTag::Char)); char::from_u32(ptr.1 .0 .0 as u32) } pub(crate) fn fetch_fun(&self, ptr: &Ptr) -> Option<&(Ptr, Ptr, Ptr)> { - debug_assert!(matches!(ptr.0, Tag::Fun)); + debug_assert!(matches!(ptr.0, ExprTag::Fun)); if ptr.1.is_opaque() { None // Some(&self.opaque_fun) @@ -1788,7 +1601,7 @@ impl Store { } pub(crate) fn fetch_cons(&self, ptr: &Ptr) -> Option<&(Ptr, Ptr)> { - debug_assert!(matches!(ptr.0, Tag::Cons)); + debug_assert!(matches!(ptr.0, ExprTag::Cons)); if ptr.1.is_opaque() { None } else { @@ -1797,7 +1610,7 @@ impl Store { } pub(crate) fn fetch_comm(&self, ptr: &Ptr) -> Option<&(FWrap, Ptr)> { - debug_assert!(matches!(ptr.0, Tag::Comm)); + debug_assert!(matches!(ptr.0, ExprTag::Comm)); if ptr.1.is_opaque() { None } else { @@ -1806,20 +1619,20 @@ impl Store { } pub(crate) fn fetch_num(&self, ptr: &Ptr) -> Option<&Num> { - debug_assert!(matches!(ptr.0, Tag::Num)); + debug_assert!(matches!(ptr.0, ExprTag::Num)); self.num_store.get_index(ptr.1.idx()) } fn fetch_thunk(&self, ptr: &Ptr) -> Option<&Thunk> { - debug_assert!(matches!(ptr.0, Tag::Thunk)); + debug_assert!(matches!(ptr.0, ExprTag::Thunk)); self.thunk_store.get_index(ptr.1.idx()) } pub(crate) fn fetch_uint(&self, ptr: &Ptr) -> Option { // If more UInt variants are added, the following assertion should be relaxed to check for any of them. - debug_assert!(matches!(ptr.0, Tag::U64)); + debug_assert!(matches!(ptr.0, ExprTag::U64)); match ptr.0 { - Tag::U64 => Some(UInt::U64(ptr.1 .0 .0 as u64)), + ExprTag::U64 => Some(UInt::U64(ptr.1 .0 .0 as u64)), _ => unreachable!(), } } @@ -1829,19 +1642,19 @@ impl Store { return Some(Expression::Opaque(*ptr)); } match ptr.0 { - Tag::Nil => Some(Expression::Nil), - Tag::Cons => self.fetch_cons(ptr).map(|(a, b)| Expression::Cons(*a, *b)), - Tag::Comm => self.fetch_comm(ptr).map(|(a, b)| Expression::Comm(a.0, *b)), - Tag::Sym => self.fetch_sym(ptr).map(|sym| Expression::Sym(sym)), - Tag::Key => self.fetch_sym(ptr).map(|sym| Expression::Sym(sym)), - Tag::Num => self.fetch_num(ptr).map(|num| Expression::Num(*num)), - Tag::Fun => self + ExprTag::Nil => Some(Expression::Nil), + ExprTag::Cons => self.fetch_cons(ptr).map(|(a, b)| Expression::Cons(*a, *b)), + ExprTag::Comm => self.fetch_comm(ptr).map(|(a, b)| Expression::Comm(a.0, *b)), + ExprTag::Sym => self.fetch_sym(ptr).map(|sym| Expression::Sym(sym)), + ExprTag::Key => self.fetch_sym(ptr).map(|sym| Expression::Sym(sym)), + ExprTag::Num => self.fetch_num(ptr).map(|num| Expression::Num(*num)), + ExprTag::Fun => self .fetch_fun(ptr) .map(|(a, b, c)| Expression::Fun(*a, *b, *c)), - Tag::Thunk => self.fetch_thunk(ptr).map(|thunk| Expression::Thunk(*thunk)), - Tag::Str => self.fetch_str(ptr).map(|str| Expression::Str(str)), - Tag::Char => self.fetch_char(ptr).map(Expression::Char), - Tag::U64 => self.fetch_uint(ptr).map(Expression::UInt), + ExprTag::Thunk => self.fetch_thunk(ptr).map(|thunk| Expression::Thunk(*thunk)), + ExprTag::Str => self.fetch_str(ptr).map(|str| Expression::Str(str)), + ExprTag::Char => self.fetch_char(ptr).map(Expression::Char), + ExprTag::U64 => self.fetch_uint(ptr).map(Expression::UInt), } } @@ -1952,13 +1765,13 @@ impl Store { /// Mutable version of car_cdr to handle Str. `(cdr str)` may return a new str (the tail), which must be allocated. pub fn car_cdr_mut(&mut self, ptr: &Ptr) -> Result<(Ptr, Ptr), Error> { match ptr.0 { - Tag::Nil => Ok((self.get_nil(), self.get_nil())), - Tag::Cons => match self.fetch(ptr) { + ExprTag::Nil => Ok((self.get_nil(), self.get_nil())), + ExprTag::Cons => match self.fetch(ptr) { Some(Expression::Cons(car, cdr)) => Ok((car, cdr)), Some(Expression::Opaque(_)) => Err(Error("cannot destructure opaque Cons".into())), _ => unreachable!(), }, - Tag::Str => { + ExprTag::Str => { if let Some(Expression::Str(s)) = self.fetch(ptr) { let mut str = s.chars(); if let Some(c) = str.next() { @@ -1978,13 +1791,13 @@ impl Store { pub fn car_cdr(&self, ptr: &Ptr) -> Result<(Ptr, Ptr), Error> { match ptr.0 { - Tag::Nil => Ok((self.get_nil(), self.get_nil())), - Tag::Cons => match self.fetch(ptr) { + ExprTag::Nil => Ok((self.get_nil(), self.get_nil())), + ExprTag::Cons => match self.fetch(ptr) { Some(Expression::Cons(car, cdr)) => Ok((car, cdr)), Some(Expression::Opaque(_)) => panic!("cannot destructure opaque Cons"), _ => unreachable!(), }, - Tag::Str => { + ExprTag::Str => { if let Some(Expression::Str(s)) = self.fetch(ptr) { Ok({ let mut chars = s.chars(); @@ -2017,7 +1830,7 @@ impl Store { } pub fn hash_expr_aux(&self, ptr: &Ptr, mode: HashScalar) -> Option> { - use Tag::*; + use ExprTag::*; match ptr.tag() { Nil => self.hash_nil(mode), Cons => self.hash_cons(*ptr, mode), @@ -2399,7 +2212,7 @@ impl Store { let n = self.fetch_uint(&ptr)?; match n { - UInt::U64(x) => Some(self.scalar_ptr(ptr, F::from_u64(x)?, mode)), + UInt::U64(x) => Some(self.scalar_ptr(ptr, F::from_u64(x), mode)), } } @@ -2501,11 +2314,11 @@ impl Store { chars.fold(initial_scalar_ptr, |acc, char| { let c_scalar: F = (u32::from(char) as u64).into(); // This bypasses create_scalar_ptr but is okay because Chars are immediate and don't need to be indexed. - let c = ScalarPtr(Tag::Char.as_field(), c_scalar); + let c = ScalarPtr(ExprTag::Char.as_field(), c_scalar); let hash = self.hash_scalar_ptrs_2(&[c, acc]); // This bypasses create_scalar_ptr but is okay because we will call it to correctly create each of these // ScalarPtrs belwo, in hash_string_mut_aux. - let new_scalar_ptr = ScalarPtr(Tag::Str.as_field(), hash); + let new_scalar_ptr = ScalarPtr(ExprTag::Str.as_field(), hash); hashes.push(hash); new_scalar_ptr }); @@ -2578,7 +2391,7 @@ impl Store { // TODO: May need new tag for this. // Meanwhile, it is illegal to try to dereference/follow an opaque PTR. // So any tag and RawPtr are okay. - Ptr(Tag::Nil, self.new_opaque_raw_ptr()) + Ptr(ExprTag::Nil, self.new_opaque_raw_ptr()) } pub fn new_opaque_raw_ptr(&mut self) -> RawPtr { @@ -2600,8 +2413,8 @@ impl Store { } pub fn cons_eq(&self, a: &Ptr, b: &Ptr) -> bool { - assert_eq!(Tag::Cons, a.tag()); - assert_eq!(Tag::Cons, b.tag()); + assert_eq!(ExprTag::Cons, a.tag()); + assert_eq!(ExprTag::Cons, b.tag()); let a_opaque = a.is_opaque(); let b_opaque = b.is_opaque(); @@ -2714,52 +2527,13 @@ pub mod test { use super::*; use quickcheck::{Arbitrary, Gen}; - use crate::test::frequency; - use libipld::serde::from_ipld; use libipld::serde::to_ipld; use libipld::Ipld; - impl Arbitrary for Tag { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Tag>)> = vec![ - (100, Box::new(|_| Tag::Nil)), - (100, Box::new(|_| Tag::Cons)), - (100, Box::new(|_| Tag::Sym)), - (100, Box::new(|_| Tag::Fun)), - (100, Box::new(|_| Tag::Num)), - (100, Box::new(|_| Tag::Thunk)), - (100, Box::new(|_| Tag::Str)), - ]; - frequency(g, input) - } - } - impl Arbitrary for ContTag { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box ContTag>)> = vec![ - (100, Box::new(|_| ContTag::Outermost)), - (100, Box::new(|_| ContTag::Call)), - (100, Box::new(|_| ContTag::Call2)), - (100, Box::new(|_| ContTag::Tail)), - (100, Box::new(|_| ContTag::Error)), - (100, Box::new(|_| ContTag::Lookup)), - (100, Box::new(|_| ContTag::Unop)), - (100, Box::new(|_| ContTag::Binop)), - (100, Box::new(|_| ContTag::Binop2)), - (100, Box::new(|_| ContTag::If)), - (100, Box::new(|_| ContTag::Let)), - (100, Box::new(|_| ContTag::LetRec)), - (100, Box::new(|_| ContTag::Dummy)), - (100, Box::new(|_| ContTag::Terminal)), - (100, Box::new(|_| ContTag::Emit)), - ]; - frequency(g, input) - } - } - impl Arbitrary for ScalarPtr { fn arbitrary(g: &mut Gen) -> Self { - let tag = Tag::arbitrary(g); + let tag = ExprTag::arbitrary(g); let val = FWrap::arbitrary(g); ScalarPtr::from_parts(Fr::from(tag as u64), val.0) } @@ -2778,14 +2552,14 @@ pub mod test { } } - #[test] - fn unit_scalar_ptr_ipld() { - let tag = Tag::Num.as_field(); - let dig = 0.into(); - let ptr = ScalarPtr::::from_parts(tag, dig); - let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); - assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) - } + //#[test] + //fn unit_scalar_ptr_ipld() { + // let tag = ExprTag::Num.as_field(); + // let dig = 0.into(); + // let ptr = ScalarPtr::::from_parts(tag, dig); + // let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); + // assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) + //} impl Arbitrary for ScalarContPtr { fn arbitrary(g: &mut Gen) -> Self { @@ -2808,32 +2582,14 @@ pub mod test { } } - #[test] - fn unit_scalar_cont_ptr_ipld() { - let tag = ContTag::Dummy.as_field(); - let dig = 0.into(); - let ptr = ScalarContPtr::::from_parts(tag, dig); - let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); - assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) - } - - impl Arbitrary for Op1 { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Op1>)> = vec![ - (100, Box::new(|_| Op1::Car)), - (100, Box::new(|_| Op1::Cdr)), - (100, Box::new(|_| Op1::Atom)), - (100, Box::new(|_| Op1::Emit)), - (100, Box::new(|_| Op1::Secret)), - (100, Box::new(|_| Op1::Commit)), - (100, Box::new(|_| Op1::Num)), - (100, Box::new(|_| Op1::Comm)), - (100, Box::new(|_| Op1::Char)), - (100, Box::new(|_| Op1::Eval)), - ]; - frequency(g, input) - } - } + //#[test] + //fn unit_scalar_cont_ptr_ipld() { + // let tag = ContTag::Dummy.as_field(); + // let dig = 0.into(); + // let ptr = ScalarContPtr::::from_parts(tag, dig); + // let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); + // assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) + //} #[quickcheck] fn prop_op1_ipld(x: Op1) -> bool { @@ -2856,29 +2612,6 @@ pub mod test { ); } - impl Arbitrary for Op2 { - fn arbitrary(g: &mut Gen) -> Self { - let input: Vec<(i64, Box Op2>)> = vec![ - (100, Box::new(|_| Op2::Sum)), - (100, Box::new(|_| Op2::Diff)), - (100, Box::new(|_| Op2::Product)), - (100, Box::new(|_| Op2::Quotient)), - (100, Box::new(|_| Op2::Equal)), - (100, Box::new(|_| Op2::NumEqual)), - (100, Box::new(|_| Op2::Less)), - (100, Box::new(|_| Op2::Greater)), - (100, Box::new(|_| Op2::LessEqual)), - (100, Box::new(|_| Op2::GreaterEqual)), - (100, Box::new(|_| Op2::Cons)), - (100, Box::new(|_| Op2::StrCons)), - (100, Box::new(|_| Op2::Begin)), - (100, Box::new(|_| Op2::Hide)), - (100, Box::new(|_| Op2::Eval)), - ]; - frequency(g, input) - } - } - #[quickcheck] fn prop_op2_ipld_embed(x: Op2) -> bool { if let Ok(ipld) = to_ipld(x) { @@ -2910,17 +2643,17 @@ pub mod test { #[test] fn tag_vals() { - assert_eq!(0, Tag::Nil as u64); - assert_eq!(1, Tag::Cons as u64); - assert_eq!(2, Tag::Sym as u64); - assert_eq!(3, Tag::Fun as u64); - assert_eq!(4, Tag::Num as u64); - assert_eq!(5, Tag::Thunk as u64); - assert_eq!(6, Tag::Str as u64); - assert_eq!(7, Tag::Char as u64); - assert_eq!(8, Tag::Comm as u64); - assert_eq!(9, Tag::U64 as u64); - assert_eq!(10, Tag::Key as u64); + assert_eq!(0, ExprTag::Nil as u64); + assert_eq!(1, ExprTag::Cons as u64); + assert_eq!(2, ExprTag::Sym as u64); + assert_eq!(3, ExprTag::Fun as u64); + assert_eq!(4, ExprTag::Num as u64); + assert_eq!(5, ExprTag::Thunk as u64); + assert_eq!(6, ExprTag::Str as u64); + assert_eq!(7, ExprTag::Char as u64); + assert_eq!(8, ExprTag::Comm as u64); + assert_eq!(9, ExprTag::U64 as u64); + assert_eq!(10, ExprTag::Key as u64); } #[test] @@ -3309,7 +3042,7 @@ pub mod test { let sym_tag = s.get_expr_hash(&sym).unwrap().0; // let sym_hash = s.get_expr_hash(&sym).unwrap().1; - assert_eq!(Tag::Sym.as_field::(), sym_tag); + assert_eq!(ExprTag::Sym.as_field::(), sym_tag); // FIXME: What should this be? Should this even be allowed? // assert_eq!(Fr::from(0), sym_hash) diff --git a/src/tag.rs b/src/tag.rs new file mode 100644 index 0000000000..9c7ffbd52d --- /dev/null +++ b/src/tag.rs @@ -0,0 +1,492 @@ +use std::{convert::TryFrom, fmt}; + +use serde_repr::{Deserialize_repr, Serialize_repr}; + +use crate::field::LurkField; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u16)] +pub enum ExprTag { + Nil = 0b0000_0000_0000_0000, + Cons, + Sym, + Fun, + Num, + Thunk, + Str, + Char, + Comm, + U64, + Key, +} + +impl From for u16 { + fn from(val: ExprTag) -> Self { + val as u16 + } +} + +impl From for u64 { + fn from(val: ExprTag) -> Self { + val as u64 + } +} + +impl TryFrom for ExprTag { + type Error = String; + + fn try_from(x: u16) -> Result>::Error> { + match x { + f if f == ExprTag::Nil as u16 => Ok(ExprTag::Nil), + f if f == ExprTag::Cons as u16 => Ok(ExprTag::Cons), + f if f == ExprTag::Sym as u16 => Ok(ExprTag::Sym), + f if f == ExprTag::Fun as u16 => Ok(ExprTag::Fun), + f if f == ExprTag::Thunk as u16 => Ok(ExprTag::Thunk), + f if f == ExprTag::Num as u16 => Ok(ExprTag::Num), + f if f == ExprTag::Str as u16 => Ok(ExprTag::Str), + f if f == ExprTag::Char as u16 => Ok(ExprTag::Char), + f if f == ExprTag::Comm as u16 => Ok(ExprTag::Comm), + f if f == ExprTag::U64 as u16 => Ok(ExprTag::U64), + f if f == ExprTag::Key as u16 => Ok(ExprTag::Key), + f => Err(format!("Invalid ExprTag value: {}", f)), + } + } +} + +impl fmt::Display for ExprTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ExprTag::Nil => write!(f, "nil#"), + ExprTag::Cons => write!(f, "cons#"), + ExprTag::Sym => write!(f, "sym#"), + ExprTag::Fun => write!(f, "fun#"), + ExprTag::Num => write!(f, "num#"), + ExprTag::Thunk => write!(f, "thunk#"), + ExprTag::Str => write!(f, "str#"), + ExprTag::Key => write!(f, "key#"), + ExprTag::Char => write!(f, "char#"), + ExprTag::Comm => write!(f, "comm#"), + ExprTag::U64 => write!(f, "u64#"), + } + } +} + +impl ExprTag { + pub fn from_field(f: &F) -> Option { + Self::try_from(f.to_u16()?).ok() + } + + pub fn as_field(&self) -> F { + F::from(*self as u64) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(u16)] +pub enum ContTag { + Outermost = 0b0001_0000_0000_0000, + Call0, + Call, + Call2, + Tail, + Error, + Lookup, + Unop, + Binop, + Binop2, + If, + Let, + LetRec, + Dummy, + Terminal, + Emit, +} + +impl From for u16 { + fn from(val: ContTag) -> Self { + val as u16 + } +} + +impl From for u64 { + fn from(val: ContTag) -> Self { + val as u64 + } +} + +impl TryFrom for ContTag { + type Error = String; + + fn try_from(x: u16) -> Result>::Error> { + match x { + f if f == ContTag::Outermost as u16 => Ok(ContTag::Outermost), + f if f == ContTag::Call0 as u16 => Ok(ContTag::Call0), + f if f == ContTag::Call as u16 => Ok(ContTag::Call), + f if f == ContTag::Call2 as u16 => Ok(ContTag::Call2), + f if f == ContTag::Tail as u16 => Ok(ContTag::Tail), + f if f == ContTag::Error as u16 => Ok(ContTag::Error), + f if f == ContTag::Lookup as u16 => Ok(ContTag::Lookup), + f if f == ContTag::Unop as u16 => Ok(ContTag::Unop), + f if f == ContTag::Binop as u16 => Ok(ContTag::Binop), + f if f == ContTag::Binop2 as u16 => Ok(ContTag::Binop2), + f if f == ContTag::If as u16 => Ok(ContTag::If), + f if f == ContTag::Let as u16 => Ok(ContTag::Let), + f if f == ContTag::LetRec as u16 => Ok(ContTag::LetRec), + f if f == ContTag::Dummy as u16 => Ok(ContTag::Dummy), + f if f == ContTag::Terminal as u16 => Ok(ContTag::Terminal), + f if f == ContTag::Emit as u16 => Ok(ContTag::Emit), + f => Err(format!("Invalid ContTag value: {}", f)), + } + } +} + +impl ContTag { + pub fn from_field(f: &F) -> Option { + Self::try_from(f.to_u16()?).ok() + } + + pub fn as_field(&self) -> F { + F::from(*self as u64) + } +} + +impl fmt::Display for ContTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ContTag::Outermost => write!(f, "outermost#"), + ContTag::Call0 => write!(f, "call0#"), + ContTag::Call => write!(f, "call#"), + ContTag::Call2 => write!(f, "call2#"), + ContTag::Tail => write!(f, "tail#"), + ContTag::Error => write!(f, "error#"), + ContTag::Lookup => write!(f, "lookup#"), + ContTag::Unop => write!(f, "unop#"), + ContTag::Binop => write!(f, "binop#"), + ContTag::Binop2 => write!(f, "binop2#"), + ContTag::If => write!(f, "if#"), + ContTag::Let => write!(f, "let#"), + ContTag::LetRec => write!(f, "letrec#"), + ContTag::Dummy => write!(f, "dummy#"), + ContTag::Terminal => write!(f, "terminal#"), + ContTag::Emit => write!(f, "emit#"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u16)] +pub enum Op1 { + Car = 0b0010_0000_0000_0000, + Cdr, + Atom, + Emit, + Open, + Secret, + Commit, + Num, + Comm, + Char, + Eval, + U64, +} + +impl From for u16 { + fn from(val: Op1) -> Self { + val as u16 + } +} + +impl From for u64 { + fn from(val: Op1) -> Self { + val as u64 + } +} + +impl TryFrom for Op1 { + type Error = String; + + fn try_from(x: u16) -> Result>::Error> { + match x { + f if f == Op1::Car as u16 => Ok(Op1::Car), + f if f == Op1::Cdr as u16 => Ok(Op1::Cdr), + f if f == Op1::Atom as u16 => Ok(Op1::Atom), + f if f == Op1::Emit as u16 => Ok(Op1::Emit), + f if f == Op1::Open as u16 => Ok(Op1::Open), + f if f == Op1::Secret as u16 => Ok(Op1::Secret), + f if f == Op1::Commit as u16 => Ok(Op1::Commit), + f if f == Op1::Num as u16 => Ok(Op1::Num), + f if f == Op1::Comm as u16 => Ok(Op1::Comm), + f if f == Op1::Char as u16 => Ok(Op1::Char), + f if f == Op1::Eval as u16 => Ok(Op1::Eval), + f if f == Op1::U64 as u16 => Ok(Op1::U64), + f => Err(format!("Invalid Op1 value: {}", f)), + } + } +} + +impl Op1 { + pub fn from_field(f: &F) -> Option { + Self::try_from(f.to_u16()?).ok() + } + + pub fn as_field(&self) -> F { + F::from(*self as u64) + } +} + +impl fmt::Display for Op1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Op1::Car => write!(f, "car#"), + Op1::Cdr => write!(f, "cdr#"), + Op1::Atom => write!(f, "atom#"), + Op1::Emit => write!(f, "emit#"), + Op1::Open => write!(f, "open#"), + Op1::Secret => write!(f, "secret#"), + Op1::Commit => write!(f, "commit#"), + Op1::Num => write!(f, "num#"), + Op1::Comm => write!(f, "comm#"), + Op1::Char => write!(f, "char#"), + Op1::Eval => write!(f, "eval#"), + Op1::U64 => write!(f, "u64#"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u16)] +pub enum Op2 { + Sum = 0b0011_0000_0000_0000, + Diff, + Product, + Quotient, + Equal, + NumEqual, + Less, + Greater, + LessEqual, + GreaterEqual, + Cons, + StrCons, + Begin, + Hide, + Modulo, + Eval, +} + +impl From for u16 { + fn from(val: Op2) -> Self { + val as u16 + } +} + +impl From for u64 { + fn from(val: Op2) -> Self { + val as u64 + } +} + +impl TryFrom for Op2 { + type Error = String; + + fn try_from(x: u16) -> Result>::Error> { + match x { + f if f == Op2::Sum as u16 => Ok(Op2::Sum), + f if f == Op2::Diff as u16 => Ok(Op2::Diff), + f if f == Op2::Product as u16 => Ok(Op2::Product), + f if f == Op2::Quotient as u16 => Ok(Op2::Quotient), + f if f == Op2::Equal as u16 => Ok(Op2::Equal), + f if f == Op2::NumEqual as u16 => Ok(Op2::NumEqual), + f if f == Op2::Less as u16 => Ok(Op2::Less), + f if f == Op2::Greater as u16 => Ok(Op2::Greater), + f if f == Op2::LessEqual as u16 => Ok(Op2::LessEqual), + f if f == Op2::GreaterEqual as u16 => Ok(Op2::GreaterEqual), + f if f == Op2::Cons as u16 => Ok(Op2::Cons), + f if f == Op2::StrCons as u16 => Ok(Op2::StrCons), + f if f == Op2::Begin as u16 => Ok(Op2::Begin), + f if f == Op2::Hide as u16 => Ok(Op2::Hide), + f if f == Op2::Modulo as u16 => Ok(Op2::Modulo), + f if f == Op2::Eval as u16 => Ok(Op2::Eval), + f => Err(format!("Invalid Op2 value: {}", f)), + } + } +} + +impl Op2 { + pub fn rom_field(f: &F) -> Option { + Self::try_from(f.to_u16()?).ok() + } + + pub fn as_field + ff::Field>(&self) -> F { + F::from(*self as u64) + } + + pub fn is_numeric(&self) -> bool { + matches!( + self, + Op2::Sum + | Op2::Diff + | Op2::Product + | Op2::Quotient + | Op2::Less + | Op2::Greater + | Op2::LessEqual + | Op2::GreaterEqual + | Op2::NumEqual + | Op2::Modulo + ) + } +} + +impl fmt::Display for Op2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Op2::Sum => write!(f, "sum#"), + Op2::Diff => write!(f, "diff#"), + Op2::Product => write!(f, "product#"), + Op2::Quotient => write!(f, "quotient#"), + Op2::Equal => write!(f, "equal#"), + Op2::NumEqual => write!(f, "numequal#"), + Op2::Less => write!(f, "less#"), + Op2::Greater => write!(f, "greater"), + Op2::LessEqual => write!(f, "lessequal#"), + Op2::GreaterEqual => write!(f, "greaterequal#"), + Op2::Cons => write!(f, "cons"), + Op2::StrCons => write!(f, "strcons#"), + Op2::Begin => write!(f, "begin"), + Op2::Hide => write!(f, "hide"), + Op2::Modulo => write!(f, "modulo"), + Op2::Eval => write!(f, "eval#"), + } + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use crate::test::frequency; + use quickcheck::{Arbitrary, Gen}; + + impl Arbitrary for ExprTag { + fn arbitrary(g: &mut Gen) -> Self { + let input: Vec<(i64, Box ExprTag>)> = vec![ + (100, Box::new(|_| ExprTag::Nil)), + (100, Box::new(|_| ExprTag::Cons)), + (100, Box::new(|_| ExprTag::Sym)), + (100, Box::new(|_| ExprTag::Fun)), + (100, Box::new(|_| ExprTag::Num)), + (100, Box::new(|_| ExprTag::Thunk)), + (100, Box::new(|_| ExprTag::Str)), + (100, Box::new(|_| ExprTag::Char)), + (100, Box::new(|_| ExprTag::Comm)), + (100, Box::new(|_| ExprTag::U64)), + (100, Box::new(|_| ExprTag::Key)), + ]; + frequency(g, input) + } + } + impl Arbitrary for ContTag { + fn arbitrary(g: &mut Gen) -> Self { + let input: Vec<(i64, Box ContTag>)> = vec![ + (100, Box::new(|_| ContTag::Outermost)), + (100, Box::new(|_| ContTag::Call)), + (100, Box::new(|_| ContTag::Call2)), + (100, Box::new(|_| ContTag::Tail)), + (100, Box::new(|_| ContTag::Error)), + (100, Box::new(|_| ContTag::Lookup)), + (100, Box::new(|_| ContTag::Unop)), + (100, Box::new(|_| ContTag::Binop)), + (100, Box::new(|_| ContTag::Binop2)), + (100, Box::new(|_| ContTag::If)), + (100, Box::new(|_| ContTag::Let)), + (100, Box::new(|_| ContTag::LetRec)), + (100, Box::new(|_| ContTag::Dummy)), + (100, Box::new(|_| ContTag::Terminal)), + (100, Box::new(|_| ContTag::Emit)), + ]; + frequency(g, input) + } + } + + impl Arbitrary for Op1 { + fn arbitrary(g: &mut Gen) -> Self { + let input: Vec<(i64, Box Op1>)> = vec![ + (100, Box::new(|_| Op1::Car)), + (100, Box::new(|_| Op1::Cdr)), + (100, Box::new(|_| Op1::Atom)), + (100, Box::new(|_| Op1::Emit)), + (100, Box::new(|_| Op1::Secret)), + (100, Box::new(|_| Op1::Commit)), + (100, Box::new(|_| Op1::Num)), + (100, Box::new(|_| Op1::Comm)), + (100, Box::new(|_| Op1::Char)), + (100, Box::new(|_| Op1::Eval)), + ]; + frequency(g, input) + } + } + + impl Arbitrary for Op2 { + fn arbitrary(g: &mut Gen) -> Self { + let input: Vec<(i64, Box Op2>)> = vec![ + (100, Box::new(|_| Op2::Sum)), + (100, Box::new(|_| Op2::Diff)), + (100, Box::new(|_| Op2::Product)), + (100, Box::new(|_| Op2::Quotient)), + (100, Box::new(|_| Op2::Equal)), + (100, Box::new(|_| Op2::NumEqual)), + (100, Box::new(|_| Op2::Less)), + (100, Box::new(|_| Op2::Greater)), + (100, Box::new(|_| Op2::LessEqual)), + (100, Box::new(|_| Op2::GreaterEqual)), + (100, Box::new(|_| Op2::Cons)), + (100, Box::new(|_| Op2::StrCons)), + (100, Box::new(|_| Op2::Begin)), + (100, Box::new(|_| Op2::Hide)), + (100, Box::new(|_| Op2::Eval)), + ]; + frequency(g, input) + } + } + + #[quickcheck] + fn prop_expr_tag_into_u16(t: ExprTag) -> bool { + let x: u16 = t.into(); + if let Ok(t2) = ExprTag::try_from(x) { + t2 == t + } else { + false + } + } + + #[quickcheck] + fn prop_cont_tag_into_u16(t: ContTag) -> bool { + let x: u16 = t.into(); + if let Ok(t2) = ContTag::try_from(x) { + t2 == t + } else { + false + } + } + + #[quickcheck] + fn prop_op1_into_u16(t: Op1) -> bool { + let x: u16 = t.into(); + if let Ok(t2) = Op1::try_from(x) { + t2 == t + } else { + false + } + } + + #[quickcheck] + fn prop_op2_into_u16(t: Op2) -> bool { + let x: u16 = t.into(); + if let Ok(t2) = Op2::try_from(x) { + t2 == t + } else { + false + } + } +} From 4d1f59b560c39d0503682f2a2d1918dfee17b68d Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 19:29:43 -0500 Subject: [PATCH 12/21] fix flake --- flake.nix | 6 ------ 1 file changed, 6 deletions(-) diff --git a/flake.nix b/flake.nix index 0287062dfb..a739ed47a7 100644 --- a/flake.nix +++ b/flake.nix @@ -21,14 +21,8 @@ toolchain = with fenix.packages.${system}; fromToolchainFile { file = ./rust-toolchain.toml; # alternatively, dir = ./.; -<<<<<<< HEAD - sha256 = "sha256-/F36bL5WoJ7opVs7o96dwVHE9SEt3am+6N3jPygJRKY="; - - }; -======= sha256 = "sha256-riZUc+R9V35c/9e8KJUE+8pzpXyl0lRXt3ZkKlxoY0g="; }; ->>>>>>> 30c8fd43e21b14b6cdcf7c5ea7613e43fbbfe456 in rec { defaultPackage = (naersk.lib.${system}.override { From 0873ce07259e1bd28f907189e1de6d0142e8e742 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 19:35:33 -0500 Subject: [PATCH 13/21] fix formatting --- src/lib.rs | 56 +++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 980300f9ca..99124494c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,45 +29,37 @@ pub mod writer; mod error; mod num; pub use num::Num; -pub use sym::{ - Sym, - Symbol, -}; +pub use sym::{Sym, Symbol}; 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, + 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; + 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>( - g: &mut Gen, - gens: Vec<(i64, F)>, - ) -> T { - if gens.iter().any(|(v, _)| *v < 0) { - panic!("Negative weight"); + // 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>(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); } - 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); - } } From 1e1676c103a7fb6bd1cf5d63adf11bb97817ed07 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 20:44:32 -0500 Subject: [PATCH 14/21] fix fcomm --- fcomm/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fcomm/src/lib.rs b/fcomm/src/lib.rs index 590626f780..e85c4a7e7f 100644 --- a/fcomm/src/lib.rs +++ b/fcomm/src/lib.rs @@ -27,7 +27,8 @@ use lurk::{ Prover, }, scalar_store::ScalarStore, - store::{Pointer, Ptr, ScalarPointer, ScalarPtr, Store, Tag}, + store::{Pointer, Ptr, ScalarPointer, ScalarPtr, Store}, + tag::ExprTag, writer::Write, }; use once_cell::sync::OnceCell; @@ -482,7 +483,7 @@ impl Commitment { pub fn from_comm(s: &mut Store, ptr: &Ptr) -> Self { let digest = *s.hash_expr(ptr).expect("couldn't hash ptr").value(); - assert_eq!(Tag::Comm, ptr.tag()); + assert_eq!(ExprTag::Comm, ptr.tag()); Commitment { comm: digest } } From fee2a99b8225379c31ea697f1c4b0e66f65ce531 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 31 Jan 2023 21:15:17 -0500 Subject: [PATCH 15/21] fix clippy warnings --- src/lib.rs | 1 + src/store.rs | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 99124494c4..f4457d9a76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::single_match, clippy::type_complexity)] +#![allow(clippy::uninlined_format_args)] extern crate core; diff --git a/src/store.rs b/src/store.rs index 5ea71aa948..9475a9b7c1 100644 --- a/src/store.rs +++ b/src/store.rs @@ -311,11 +311,8 @@ impl Serialize for ScalarPtr { // magic numbers to avoid multicodec table collisons // this will disappear when we move from IPLD to LDON let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); - let hash = Multihash::wrap(codec, &val.to_repr_bytes()).or_else(|_| { - Err(S::Error::custom( - "expected validly tagged ScalarPtr".to_string(), - )) - })?; + let hash = Multihash::wrap(codec, &val.to_repr_bytes()) + .map_err(|_| S::Error::custom("expected validly tagged ScalarPtr".to_string()))?; let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) } @@ -403,11 +400,8 @@ impl Serialize for ScalarContPtr { // magic numbers to avoid multicodec table collisons // this will disappear when we move from IPLD to LDON let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); - let hash = Multihash::wrap(codec, &val.to_repr_bytes()).or_else(|_| { - Err(S::Error::custom( - "expected validly tagged ScalarContPtr".to_string(), - )) - })?; + let hash = Multihash::wrap(codec, &val.to_repr_bytes()) + .map_err(|_| S::Error::custom("expected validly tagged ScalarContPtr".to_string()))?; let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) } From 9627babc6e0b4ce8809f98ae0f0b6fa6bdc2c519 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Thu, 2 Feb 2023 03:03:11 -0500 Subject: [PATCH 16/21] remove unnecessary canonical bytes trait methods, add repr canonicity tests --- src/circuit/circuit_frame.rs | 2 +- src/field.rs | 139 +++++++++++++++++++++-------------- src/store.rs | 8 +- 3 files changed, 89 insertions(+), 60 deletions(-) diff --git a/src/circuit/circuit_frame.rs b/src/circuit/circuit_frame.rs index 17de457ad6..ff06b63da9 100644 --- a/src/circuit/circuit_frame.rs +++ b/src/circuit/circuit_frame.rs @@ -4857,7 +4857,7 @@ pub fn allocate_unconstrained_bignum>( let bytes_le = bn.to_bytes_le(); let mut bytes_padded = [0u8; 32]; bytes_padded[0..bytes_le.len()].copy_from_slice(&bytes_le); - let ff = F::from_repr_bytes(&bytes_padded).unwrap(); + let ff = F::from_bytes(&bytes_padded).unwrap(); let num = AllocatedNum::alloc(&mut cs.namespace(|| "num"), || Ok(ff)).unwrap(); Ok(num) } diff --git a/src/field.rs b/src/field.rs index 0535f7f48e..e945d90e7d 100644 --- a/src/field.rs +++ b/src/field.rs @@ -15,66 +15,29 @@ pub enum LanguageField { pub trait LurkField: PrimeField + PrimeFieldBits { const FIELD: LanguageField; - fn from_repr_bytes(bs: &[u8]) -> Option { + fn to_bytes(self) -> Vec { + let repr = self.to_repr(); + repr.as_ref().to_vec() + } + fn from_bytes(bs: &[u8]) -> Option { let mut def: Self::Repr = Self::default().to_repr(); def.as_mut().copy_from_slice(bs); Self::from_repr(def).into() } - // Construct bytes from a field element *ignoring* the trait specific - // implementation of Repr - fn to_le_bytes_canonical(self) -> Vec { - let mut vec = vec![]; - let bits = self.to_le_bits(); - - let len = bits.len(); - let len_bytes = if len % 8 != 0 { len / 8 + 1 } else { len / 8 }; - for _ in 0..len_bytes { - vec.push(0u8) - } - for (n, b) in bits.into_iter().enumerate() { - let (byte_i, bit_i) = (n / 8, n % 8); - if b { - vec[byte_i] += 1u8 << bit_i; - } - } - vec - } - fn hex_digits(self) -> String { let mut s = String::new(); - let bytes = self.to_le_bytes_canonical(); + let bytes = self.to_bytes(); for b in bytes.iter().rev() { s.push_str(&format!("{:02x?}", b)); } s } - // Construct field element from possibly canonical bytes - fn from_le_bytes_canonical(bs: &[u8]) -> Self { - let mut res = Self::zero(); - let mut bs = bs.iter().rev().peekable(); - while let Some(b) = bs.next() { - let b: Self = (*b as u64).into(); - if bs.peek().is_none() { - res.add_assign(b) - } else { - res.add_assign(b); - res.mul_assign(Self::from(256u64)); - } - } - res - } - - fn to_repr_bytes(self) -> Vec { - let repr = self.to_repr(); - repr.as_ref().to_vec() - } - fn vec_f_to_bytes(vec_f: Vec) -> Vec { let mut vec = vec![]; for f in vec_f { - for byte in f.to_repr_bytes() { + for byte in f.to_bytes() { vec.push(byte) } } @@ -85,7 +48,7 @@ pub trait LurkField: PrimeField + PrimeFieldBits { let num_bytes: usize = (Self::NUM_BITS / 8 + 1) as usize; let mut vec_f: Vec = vec![]; for chunk in vec.chunks(num_bytes) { - let f: Self = Self::from_repr_bytes(chunk)?; + let f: Self = Self::from_bytes(chunk)?; vec_f.push(f); } Some(vec_f) @@ -269,7 +232,7 @@ impl<'de, F: LurkField> Deserialize<'de> for FWrap { { use serde::de::Error; let bytes: Vec = Vec::deserialize(deserializer)?; - let f = F::from_repr_bytes(&bytes).ok_or_else(|| { + let f = F::from_bytes(&bytes).ok_or_else(|| { D::Error::custom(format!("expected field element as bytes, got {:?}", &bytes)) })?; Ok(FWrap(f)) @@ -302,20 +265,86 @@ pub mod tests { } #[quickcheck] - fn prop_repr_bytes_consistency(f1: FWrap) -> bool { + fn prop_blstrs_repr_bytes_consistency(f1: FWrap) -> bool { + let bytes = f1.0.to_repr().as_ref().to_owned(); + let f2 = ::from_bytes(&bytes); + Some(f1.0) == f2 + } + + #[quickcheck] + fn prop_pallas_repr_bytes_consistency(f1: FWrap) -> bool { let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_repr_bytes(&bytes); + let f2 = ::from_bytes(&bytes); Some(f1.0) == f2 } #[quickcheck] - fn prop_byte_digits_consistency(f1: FWrap) -> bool { - let bytes = f1.0.to_le_bytes_canonical(); - let f2 = Fr::from_le_bytes_canonical(&bytes); - println!("{:?}", bytes); - println!("f1 0x{}", f1.0.hex_digits()); - println!("f2 0x{}", f2.hex_digits()); - Some(f1.0) == Some(f2) + fn prop_vesta_repr_bytes_consistency(f1: FWrap) -> bool { + let bytes = f1.0.to_repr().as_ref().to_owned(); + let f2 = ::from_bytes(&bytes); + Some(f1.0) == f2 + } + + // Construct canonical bytes from a field element + fn to_le_bytes_canonical(f: F) -> Vec { + let mut vec = vec![]; + let bits = f.to_le_bits(); + + let len = bits.len(); + let len_bytes = if len % 8 != 0 { len / 8 + 1 } else { len / 8 }; + for _ in 0..len_bytes { + vec.push(0u8) + } + for (n, b) in bits.into_iter().enumerate() { + let (byte_i, bit_i) = (n / 8, n % 8); + if b { + vec[byte_i] += 1u8 << bit_i; + } + } + vec + } + + // Construct field element from possibly canonical bytes + fn from_le_bytes_canonical(bs: &[u8]) -> F { + let mut res = F::zero(); + let mut bs = bs.iter().rev().peekable(); + while let Some(b) = bs.next() { + let b: F = (*b as u64).into(); + if bs.peek().is_none() { + res.add_assign(b) + } else { + res.add_assign(b); + res.mul_assign(F::from(256u64)); + } + } + res + } + + #[quickcheck] + fn prop_blstrs_repr_canonicity(f1: FWrap) -> bool { + let repr_bytes = f1.0.to_bytes(); + let canonical_bytes = to_le_bytes_canonical(f1.0); + let f2_repr = Fr::from_bytes(&repr_bytes).unwrap(); + let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); + repr_bytes == canonical_bytes && f2_repr == f2_canonical + } + + #[quickcheck] + fn prop_pallas_repr_canonicity(f1: FWrap) -> bool { + let repr_bytes = f1.0.to_bytes(); + let canonical_bytes = to_le_bytes_canonical(f1.0); + let f2_repr = pasta_curves::Fp::from_bytes(&repr_bytes).unwrap(); + let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); + repr_bytes == canonical_bytes && f2_repr == f2_canonical + } + + #[quickcheck] + fn prop_vesta_repr_canonicity(f1: FWrap) -> bool { + let repr_bytes = f1.0.to_bytes(); + let canonical_bytes = to_le_bytes_canonical(f1.0); + let f2_repr = pasta_curves::Fq::from_bytes(&repr_bytes).unwrap(); + let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); + repr_bytes == canonical_bytes && f2_repr == f2_canonical } #[quickcheck] diff --git a/src/store.rs b/src/store.rs index 9475a9b7c1..ddd2760591 100644 --- a/src/store.rs +++ b/src/store.rs @@ -311,7 +311,7 @@ impl Serialize for ScalarPtr { // magic numbers to avoid multicodec table collisons // this will disappear when we move from IPLD to LDON let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); - let hash = Multihash::wrap(codec, &val.to_repr_bytes()) + let hash = Multihash::wrap(codec, &val.to_bytes()) .map_err(|_| S::Error::custom("expected validly tagged ScalarPtr".to_string()))?; let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) @@ -326,7 +326,7 @@ impl<'de, F: LurkField> Deserialize<'de> for ScalarPtr { use de::Error; let cid = Cid::deserialize(deserializer)?; let tag = F::from_u64(cid.codec() & 0x0000_0000_ffff_ffff); - let val = F::from_repr_bytes(cid.hash().digest()) + let val = F::from_bytes(cid.hash().digest()) .ok_or_else(|| D::Error::custom("expected ScalarContPtr value".to_string()))?; Ok(ScalarPtr::from_parts(tag, val)) } @@ -400,7 +400,7 @@ impl Serialize for ScalarContPtr { // magic numbers to avoid multicodec table collisons // this will disappear when we move from IPLD to LDON let codec: u64 = 0x10de << 48 | tag.to_u64_unchecked(); - let hash = Multihash::wrap(codec, &val.to_repr_bytes()) + let hash = Multihash::wrap(codec, &val.to_bytes()) .map_err(|_| S::Error::custom("expected validly tagged ScalarContPtr".to_string()))?; let cid = Cid::new_v1(codec, hash); cid.serialize(serializer) @@ -415,7 +415,7 @@ impl<'de, F: LurkField> Deserialize<'de> for ScalarContPtr { use de::Error; let cid = Cid::deserialize(deserializer)?; let tag = F::from_u64(cid.codec() & 0x0000_0000_ffff_ffff); - let val = F::from_repr_bytes(cid.hash().digest()) + let val = F::from_bytes(cid.hash().digest()) .ok_or_else(|| D::Error::custom("expected ScalarContPtr value".to_string()))?; Ok(ScalarContPtr::from_parts(tag, val)) } From ce62b5fea0516e7cfb49b19e6d139e6d0ddd5716 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 7 Feb 2023 13:37:57 -0500 Subject: [PATCH 17/21] remove vec_f functions from LurkField, refactor tests --- src/field.rs | 73 ++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/src/field.rs b/src/field.rs index e945d90e7d..f07f140f46 100644 --- a/src/field.rs +++ b/src/field.rs @@ -26,34 +26,14 @@ pub trait LurkField: PrimeField + PrimeFieldBits { } fn hex_digits(self) -> String { - let mut s = String::new(); let bytes = self.to_bytes(); + let mut s = String::with_capacity(bytes.len() * 2); for b in bytes.iter().rev() { s.push_str(&format!("{:02x?}", b)); } s } - fn vec_f_to_bytes(vec_f: Vec) -> Vec { - let mut vec = vec![]; - for f in vec_f { - for byte in f.to_bytes() { - vec.push(byte) - } - } - vec - } - - fn vec_f_from_bytes(vec: &[u8]) -> Option> { - let num_bytes: usize = (Self::NUM_BITS / 8 + 1) as usize; - let mut vec_f: Vec = vec![]; - for chunk in vec.chunks(num_bytes) { - let f: Self = Self::from_bytes(chunk)?; - vec_f.push(f); - } - Some(vec_f) - } - fn to_u16(&self) -> Option { for x in &self.to_repr().as_ref()[2..] { if *x != 0 { @@ -264,25 +244,25 @@ pub mod tests { } } - #[quickcheck] - fn prop_blstrs_repr_bytes_consistency(f1: FWrap) -> bool { + fn repr_bytes_consistency(f1: FWrap) -> bool { let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_bytes(&bytes); + let f2 = ::from_bytes(&bytes); Some(f1.0) == f2 } + #[quickcheck] + fn prop_blstrs_repr_bytes_consistency(f1: FWrap) -> bool { + repr_bytes_consistency(f1) + } + #[quickcheck] fn prop_pallas_repr_bytes_consistency(f1: FWrap) -> bool { - let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_bytes(&bytes); - Some(f1.0) == f2 + repr_bytes_consistency(f1) } #[quickcheck] fn prop_vesta_repr_bytes_consistency(f1: FWrap) -> bool { - let bytes = f1.0.to_repr().as_ref().to_owned(); - let f2 = ::from_bytes(&bytes); - Some(f1.0) == f2 + repr_bytes_consistency(f1) } // Construct canonical bytes from a field element @@ -320,31 +300,27 @@ pub mod tests { res } - #[quickcheck] - fn prop_blstrs_repr_canonicity(f1: FWrap) -> bool { + fn repr_canonicity(f1: FWrap) -> bool { let repr_bytes = f1.0.to_bytes(); let canonical_bytes = to_le_bytes_canonical(f1.0); - let f2_repr = Fr::from_bytes(&repr_bytes).unwrap(); - let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); + let f2_repr = F::from_bytes(&repr_bytes).unwrap(); + let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); repr_bytes == canonical_bytes && f2_repr == f2_canonical } + #[quickcheck] + fn prop_blstrs_repr_canonicity(f1: FWrap) -> bool { + repr_canonicity(f1) + } + #[quickcheck] fn prop_pallas_repr_canonicity(f1: FWrap) -> bool { - let repr_bytes = f1.0.to_bytes(); - let canonical_bytes = to_le_bytes_canonical(f1.0); - let f2_repr = pasta_curves::Fp::from_bytes(&repr_bytes).unwrap(); - let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); - repr_bytes == canonical_bytes && f2_repr == f2_canonical + repr_canonicity(f1) } #[quickcheck] fn prop_vesta_repr_canonicity(f1: FWrap) -> bool { - let repr_bytes = f1.0.to_bytes(); - let canonical_bytes = to_le_bytes_canonical(f1.0); - let f2_repr = pasta_curves::Fq::from_bytes(&repr_bytes).unwrap(); - let f2_canonical = from_le_bytes_canonical::(&canonical_bytes); - repr_bytes == canonical_bytes && f2_repr == f2_canonical + repr_canonicity(f1) } #[quickcheck] @@ -354,13 +330,4 @@ pub mod tests { let f2 = Fr::from_expr_tag(tag); f1 == f2 && x == tag } - - #[quickcheck] - fn prop_vec_f_consistency(vec_f: VecFWrap) -> bool { - let bytes = Fr::vec_f_to_bytes(vec_f.0.clone()); - match Fr::vec_f_from_bytes(&bytes) { - Some(vec_f2) => vec_f.0 == vec_f2, - None => false, - } - } } From 59320e367b98f1aae82014107778bc115979d3a7 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 7 Feb 2023 13:40:39 -0500 Subject: [PATCH 18/21] remove no longer needed store tests --- src/store.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/store.rs b/src/store.rs index ddd2760591..908cc447d4 100644 --- a/src/store.rs +++ b/src/store.rs @@ -2546,15 +2546,6 @@ pub mod test { } } - //#[test] - //fn unit_scalar_ptr_ipld() { - // let tag = ExprTag::Num.as_field(); - // let dig = 0.into(); - // let ptr = ScalarPtr::::from_parts(tag, dig); - // let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); - // assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) - //} - impl Arbitrary for ScalarContPtr { fn arbitrary(g: &mut Gen) -> Self { let tag = ContTag::arbitrary(g); @@ -2576,15 +2567,6 @@ pub mod test { } } - //#[test] - //fn unit_scalar_cont_ptr_ipld() { - // let tag = ContTag::Dummy.as_field(); - // let dig = 0.into(); - // let ptr = ScalarContPtr::::from_parts(tag, dig); - // let cid = Cid::new_v1(Fr::to_multicodec(tag).unwrap(), Fr::to_multihash(dig)); - // assert_eq!(to_ipld(ptr).unwrap(), Ipld::Link(cid)) - //} - #[quickcheck] fn prop_op1_ipld(x: Op1) -> bool { if let Ok(ipld) = to_ipld(x) { From 054fba71c6c05e09d52b8561142cd893831def6b Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 7 Feb 2023 21:23:36 -0500 Subject: [PATCH 19/21] update FWrap Arbitrary --- src/field.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/field.rs b/src/field.rs index d85f9f0bff..9fae92b9f1 100644 --- a/src/field.rs +++ b/src/field.rs @@ -225,13 +225,17 @@ pub mod tests { use super::*; use proptest::prelude::*; + use rand::rngs::StdRng; + use rand::SeedableRng; impl Arbitrary for FWrap { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - let strategy = Just(FWrap(F::random(rand::thread_rng()))); + let strategy = any::<[u8; 32]>() + .prop_map(|seed| FWrap(F::random(StdRng::from_seed(seed)))) + .no_shrink(); strategy.boxed() } } @@ -303,7 +307,7 @@ pub mod tests { proptest! { #[test] - fn prop_bls_repr_canonicity(f1 in any::>()) { + fn prop_repr_canonicity(f1 in any::>()) { repr_canonicity(f1) } #[test] From ef566df47f678baa6979948148d2420230a1c58c Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 7 Feb 2023 22:41:22 -0500 Subject: [PATCH 20/21] Support Wasm by moving proptest to dev-dependencies --- Cargo.toml | 4 ++-- src/sym.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 372b90a891..d8cf9e76e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,8 +44,6 @@ multihash = { version = "0.16.1", default-features = false, features = ["alloc", num-bigint = "0.4.3" num-integer = "0.1.45" num-traits = "0.2.15" -proptest = "1.0.0" -proptest-derive = "0.3.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] memmap = { version = "0.5.8", package = "memmap2" } @@ -63,6 +61,8 @@ criterion = "0.3.5" structopt = { version = "0.3", default-features = false } tap = "1.0.1" rand = "0.8.4" +proptest = "1.1.0" +proptest-derive = "0.3.0" [[bench]] name = "eval" diff --git a/src/sym.rs b/src/sym.rs index dbd7e4efd4..9b77157e30 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -3,11 +3,13 @@ use crate::parser::{ }; use peekmore::PeekMore; +#[cfg(test)] use proptest_derive::Arbitrary; /// Module for symbol type, Sym. use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash, Arbitrary)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(test, derive(Arbitrary))] pub struct Symbol { pub path: Vec, // It would be better not to have this here, but it simplifies things in the Store, at least for now. From 04404ac1aeee03e4d4f66361b1efd4dc7e4f0e8d Mon Sep 17 00:00:00 2001 From: Samuel Burnham Date: Tue, 7 Feb 2023 22:48:12 -0500 Subject: [PATCH 21/21] Switch back to simple conditional compilation --- Cargo.toml | 4 ++-- src/sym.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8cf9e76e2..8098750fc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } @@ -61,8 +63,6 @@ criterion = "0.3.5" structopt = { version = "0.3", default-features = false } tap = "1.0.1" rand = "0.8.4" -proptest = "1.1.0" -proptest-derive = "0.3.0" [[bench]] name = "eval" diff --git a/src/sym.rs b/src/sym.rs index 9b77157e30..777e082aa2 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -3,13 +3,13 @@ use crate::parser::{ }; use peekmore::PeekMore; -#[cfg(test)] +#[cfg(not(target_arch = "wasm32"))] use proptest_derive::Arbitrary; /// Module for symbol type, Sym. use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] -#[cfg_attr(test, derive(Arbitrary))] +#[cfg_attr(not(target_arch = "wasm32"), derive(Arbitrary))] pub struct Symbol { pub path: Vec, // It would be better not to have this here, but it simplifies things in the Store, at least for now.