diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51692a8f..99191707 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: upload-rust: needs: [build-rust, test-rust, lint] runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' + if: github.ref_type == 'tag' steps: - uses: actions/checkout@v4 - name: Download artifacts diff --git a/.gitignore b/.gitignore index 7375672a..2c460beb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ libec_go_lib.* .vscode .code .DS_Store +*.log +*.txt +*.json +*.witness \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a40683ba..8c494aac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,18 +132,47 @@ dependencies = [ "tynm", ] +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-bls12-381" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "ark-std 0.5.0", ] +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ec" version = "0.5.0" @@ -151,9 +180,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash", - "ark-ff", - "ark-poly", - "ark-serialize", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", "ark-std 0.5.0", "educe", "fnv", @@ -165,15 +194,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", "ark-std 0.5.0", "arrayvec", "digest", @@ -185,6 +234,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -195,6 +254,19 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -208,6 +280,19 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + [[package]] name = "ark-poly" version = "0.5.0" @@ -215,27 +300,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash", - "ark-ff", - "ark-serialize", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.2", ] +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive 0.4.2", + "ark-std 0.4.0", + "digest", + "num-bigint", +] + [[package]] name = "ark-serialize" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive", + "ark-serialize-derive 0.5.0", "ark-std 0.5.0", "arrayvec", "digest", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -306,6 +414,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "big-int" version = "7.0.0" @@ -432,9 +546,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.10" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +dependencies = [ + "shlex", +] [[package]] name = "cexpr" @@ -513,8 +630,8 @@ name = "circuit-std-rs" version = "0.1.0" dependencies = [ "arith", - "ark-bls12-381", - "ark-ff", + "ark-bls12-381 0.5.0", + "ark-ff 0.5.0", "ark-std 0.4.0", "big-int", "circuit", @@ -757,6 +874,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -796,6 +924,36 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "efc" +version = "0.1.0" +dependencies = [ + "arith", + "ark-bls12-381 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", + "base64 0.22.1", + "circuit", + "circuit-std-rs", + "clap", + "config", + "expander_compiler", + "gf2", + "gkr", + "hex", + "mersenne31", + "mpi_config", + "num-bigint", + "num-traits", + "rand", + "rayon", + "serde", + "serde_json", + "sha2", + "stacker", +] + [[package]] name = "either" version = "1.13.0" @@ -1160,6 +1318,15 @@ dependencies = [ "unroll", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1181,7 +1348,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "headers-core", "http 0.2.12", @@ -1217,6 +1384,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.9" @@ -1420,9 +1593,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libffi" @@ -1834,6 +2007,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.36" @@ -1958,6 +2140,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.34" @@ -1998,6 +2189,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" + [[package]] name = "serde" version = "1.0.209" @@ -2116,6 +2313,19 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index b566927a..35cf056b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ "circuit-std-rs","expander_compiler", "expander_compiler/ec_go_lib"] +members = [ "circuit-std-rs","expander_compiler", "expander_compiler/ec_go_lib", "efc"] [profile.test] opt-level = 3 diff --git a/circuit-std-rs/src/gnark/element.rs b/circuit-std-rs/src/gnark/element.rs index e4a863d5..f21d123e 100644 --- a/circuit-std-rs/src/gnark/element.rs +++ b/circuit-std-rs/src/gnark/element.rs @@ -68,6 +68,7 @@ pub fn value_of, T: FieldParams>( let r: Element = new_const_element::(api, constant); r } + pub fn new_const_element, T: FieldParams>( api: &mut B, v: Box, @@ -75,6 +76,10 @@ pub fn new_const_element, T: FieldParams>( let fp = T::modulus(); // convert to big.Int let mut b_value = from_interface(v); + //if neg, add modulus + if b_value < BigInt::from(0) { + b_value += &fp; + } // mod reduce if fp.cmp(&b_value) != Ordering::Equal { b_value %= fp; @@ -105,7 +110,6 @@ pub fn copy(e: &Element) -> Element { } pub fn from_interface(input: Box) -> BigInt { let r; - if let Some(v) = input.downcast_ref::() { r = v.clone(); } else if let Some(v) = input.downcast_ref::() { @@ -137,6 +141,5 @@ pub fn from_interface(input: Box) -> BigInt { } else { panic!("value to BigInt not supported"); } - r } diff --git a/circuit-std-rs/src/gnark/emulated/field_bls12381/e2.rs b/circuit-std-rs/src/gnark/emulated/field_bls12381/e2.rs index a57498db..deabb4f6 100644 --- a/circuit-std-rs/src/gnark/emulated/field_bls12381/e2.rs +++ b/circuit-std-rs/src/gnark/emulated/field_bls12381/e2.rs @@ -148,6 +148,19 @@ impl Ext2 { let a1 = self.curve_f.is_zero(native, &z.a1); native.and(a0, a1) } + pub fn get_e2_sign>( + &mut self, + native: &mut B, + x: &GE2, + a0_zero_flag: Variable, + ) -> Variable { + let bit_a0 = self.curve_f.get_element_sign(native, &x.a0); + let bit_a1 = self.curve_f.get_element_sign(native, &x.a1); + let sgn2 = native.mul(a0_zero_flag, bit_a1); + let tmp0 = native.add(bit_a0, sgn2); + let tmp1 = native.mul(bit_a0, sgn2); + native.sub(tmp0, tmp1) + } pub fn add>(&mut self, native: &mut B, x: &GE2, y: &GE2) -> GE2 { let z0 = self.curve_f.add(native, &x.a0, &y.a0); let z1 = self.curve_f.add(native, &x.a1, &y.a1); @@ -257,8 +270,8 @@ impl Ext2 { inv } pub fn assert_isequal>(&mut self, native: &mut B, x: &GE2, y: &GE2) { - self.curve_f.assert_isequal(native, &x.a0, &y.a0); - self.curve_f.assert_isequal(native, &x.a1, &y.a1); + self.curve_f.assert_is_equal(native, &x.a0, &y.a0); + self.curve_f.assert_is_equal(native, &x.a1, &y.a1); } pub fn select>( &mut self, diff --git a/circuit-std-rs/src/gnark/emulated/field_bls12381/e6.rs b/circuit-std-rs/src/gnark/emulated/field_bls12381/e6.rs index e2f3972f..1bdf1b1b 100644 --- a/circuit-std-rs/src/gnark/emulated/field_bls12381/e6.rs +++ b/circuit-std-rs/src/gnark/emulated/field_bls12381/e6.rs @@ -366,12 +366,12 @@ impl Ext6 { let x3 = self.ext2.curve_f.mul_const(native, &y3, BigInt::from(6)); let x4 = self.ext2.curve_f.mul_const(native, &y4, BigInt::from(6)); let x5 = self.ext2.curve_f.mul_const(native, &y5, BigInt::from(6)); - self.ext2.curve_f.assert_isequal(native, &x[0], &x0); - self.ext2.curve_f.assert_isequal(native, &x[1], &x1); - self.ext2.curve_f.assert_isequal(native, &x[2], &x2); - self.ext2.curve_f.assert_isequal(native, &x[3], &x3); - self.ext2.curve_f.assert_isequal(native, &x[4], &x4); - self.ext2.curve_f.assert_isequal(native, &x[5], &x5); + self.ext2.curve_f.assert_is_equal(native, &x[0], &x0); + self.ext2.curve_f.assert_is_equal(native, &x[1], &x1); + self.ext2.curve_f.assert_is_equal(native, &x[2], &x2); + self.ext2.curve_f.assert_is_equal(native, &x[3], &x3); + self.ext2.curve_f.assert_is_equal(native, &x[4], &x4); + self.ext2.curve_f.assert_is_equal(native, &x[5], &x5); [y0, y1, y2, y3, y4, y5] } } diff --git a/circuit-std-rs/src/gnark/emulated/sw_bls12381/g1.rs b/circuit-std-rs/src/gnark/emulated/sw_bls12381/g1.rs index daaadfe6..f604f7ee 100644 --- a/circuit-std-rs/src/gnark/emulated/sw_bls12381/g1.rs +++ b/circuit-std-rs/src/gnark/emulated/sw_bls12381/g1.rs @@ -1,7 +1,18 @@ +use std::str::FromStr; + use crate::gnark::element::*; use crate::gnark::emparam::Bls12381Fp; use crate::gnark::emulated::field_bls12381::e2::CurveF; -use expander_compiler::frontend::*; +use crate::sha256::m31_utils::*; +use crate::utils::simple_select; +use expander_compiler::{ + declare_circuit, + frontend::{Config, Define, M31Config, RootAPI, Variable}, +}; +use num_bigint::BigInt; + +const M_COMPRESSED_SMALLEST: u8 = 0b100 << 5; +const M_COMPRESSED_LARGEST: u8 = 0b101 << 5; #[derive(Default, Clone)] pub struct G1Affine { @@ -59,4 +70,601 @@ impl G1 { G1Affine { x: xr, y: yr } } + pub fn double>(&mut self, native: &mut B, p: &G1Affine) -> G1Affine { + let xx3a = self.curve_f.mul(native, &p.x, &p.x); + let two = value_of::(native, Box::new(2)); + let three = value_of::(native, Box::new(3)); + let xx3a = self.curve_f.mul(native, &xx3a, &three); + let y1 = self.curve_f.mul(native, &p.y, &two); + let λ = self.curve_f.div(native, &xx3a, &y1); + + let x1 = self.curve_f.mul(native, &p.x, &two); + let λλ = self.curve_f.mul(native, &λ, &λ); + let xr = self.curve_f.sub(native, &λλ, &x1); + + let pxrx = self.curve_f.sub(native, &p.x, &xr); + let λpxrx = self.curve_f.mul(native, &λ, &pxrx); + let yr = self.curve_f.sub(native, &λpxrx, &p.y); + + G1Affine { x: xr, y: yr } + } + pub fn assert_is_equal>( + &mut self, + native: &mut B, + a: &G1Affine, + b: &G1Affine, + ) { + self.curve_f.assert_is_equal(native, &a.x, &b.x); + self.curve_f.assert_is_equal(native, &a.y, &b.y); + } + pub fn copy_g1>(&mut self, native: &mut B, q: &G1Affine) -> G1Affine { + let copy_q_acc_x = self.curve_f.copy(native, &q.x); + let copy_q_acc_y = self.curve_f.copy(native, &q.y); + G1Affine { + x: copy_q_acc_x, + y: copy_q_acc_y, + } + } + pub fn uncompressed>( + &mut self, + native: &mut B, + bytes: &[Variable], + ) -> G1Affine { + let mut buf_x = bytes.to_vec(); + let buf0 = to_binary(native, buf_x[0], 8); + let pad = vec![native.constant(0); 5]; + let m_data = from_binary(native, [pad, buf0[5..].to_vec()].concat()); //buf0 & mMask + let buf0_and_non_mask = from_binary(native, buf0[..5].to_vec()); //buf0 & ^mMask + buf_x[0] = buf0_and_non_mask; + + //get p.x + let rev_buf = buf_x.iter().rev().cloned().collect::>(); + let px = new_internal_element(rev_buf, 0); + + //get YSquared + let ysquared = self.curve_f.mul(native, &px, &px); + let ysquared = self.curve_f.mul(native, &ysquared, &px); + let b_curve_coeff = value_of::(native, Box::new(4)); + let ysquared = self.curve_f.add(native, &ysquared, &b_curve_coeff); + + let inputs = vec![ysquared.clone()]; + let outputs = self + .curve_f + .new_hint(native, "myhint.getelementsqrthint", 2, inputs); + + //is_square should be one + let is_square = outputs[0].clone(); + let one = self.curve_f.one_const.clone(); + self.curve_f.assert_is_equal(native, &is_square, &one); + + //get Y + let y = outputs[1].clone(); + //y^2 = ysquared + let y_squared = self.curve_f.mul(native, &y, &y); + self.curve_f.assert_is_equal(native, &y_squared, &ysquared); + + //if y is lexicographically largest + let half_fp = BigInt::from_str("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787").unwrap() / 2; + let half_fp_var = value_of::(native, Box::new(half_fp)); + let is_large = big_less_than( + native, + Bls12381Fp::bits_per_limb() as usize, + Bls12381Fp::nb_limbs() as usize, + &half_fp_var.limbs, + &y.limbs, + ); + + //if Y > -Y --> check if mData == mCompressedSmallest + //if Y <= -Y --> check if mData == mCompressedLargest + let m_compressed_largest = native.constant(M_COMPRESSED_LARGEST as u32); + let m_compressed_smallest = native.constant(M_COMPRESSED_SMALLEST as u32); + let check_m_data = simple_select( + native, + is_large, + m_compressed_smallest, + m_compressed_largest, + ); + + let check_res = native.sub(m_data, check_m_data); + let neg_flag = native.is_zero(check_res); + + let neg_y = self.curve_f.neg(native, &y); + + let y = self.curve_f.select(native, neg_flag, &neg_y, &y); + + //TBD: subgroup check, do we need to do that? Since we are pretty sure that the public key bytes are correct, its unmashalling must be on the right curve + G1Affine { x: px, y } + } + pub fn hash_to_fp>( + &mut self, + native: &mut B, + data: &[Variable], + ) -> (Element, Element) { + let u = self.curve_f.hash_to_fp(native, data, 2); + (u[0].clone(), u[1].clone()) + } + pub fn g1_isogeny>( + &mut self, + native: &mut B, + p: &G1Affine, + ) -> G1Affine { + let mut p = G1Affine { + x: p.x.my_clone(), + y: p.y.my_clone(), + }; + let den1 = self.g1_isogeny_y_denominator(native, &p.x); + let den0 = self.g1_isogeny_x_denominator(native, &p.x); + p.y = self.g1_isogeny_y_numerator(native, &p.x, &p.y); + p.x = self.g1_isogeny_x_numerator(native, &p.x); + + let den0 = self.curve_f.inverse(native, &den0); + let den1 = self.curve_f.inverse(native, &den1); + + p.x = self.curve_f.mul(native, &p.x, &den0); + p.y = self.curve_f.mul(native, &p.y, &den1); + p + } + pub fn g1_isogeny_y_denominator>( + &mut self, + native: &mut B, + x: &Element, + ) -> Element { + let coeffs = vec![ + value_of::(native, Box::new("3396434800020507717552209507749485772788165484415495716688989613875369612529138640646200921379825018840894888371137".to_string())), + value_of::(native, Box::new("3907278185868397906991868466757978732688957419873771881240086730384895060595583602347317992689443299391009456758845".to_string())), + value_of::(native, Box::new("854914566454823955479427412036002165304466268547334760894270240966182605542146252771872707010378658178126128834546".to_string())), + value_of::(native, Box::new("3496628876382137961119423566187258795236027183112131017519536056628828830323846696121917502443333849318934945158166".to_string())), + value_of::(native, Box::new("1828256966233331991927609917644344011503610008134915752990581590799656305331275863706710232159635159092657073225757".to_string())), + value_of::(native, Box::new("1362317127649143894542621413133849052553333099883364300946623208643344298804722863920546222860227051989127113848748".to_string())), + value_of::(native, Box::new("3443845896188810583748698342858554856823966611538932245284665132724280883115455093457486044009395063504744802318172".to_string())), + value_of::(native, Box::new("3484671274283470572728732863557945897902920439975203610275006103818288159899345245633896492713412187296754791689945".to_string())), + value_of::(native, Box::new("3755735109429418587065437067067640634211015783636675372165599470771975919172394156249639331555277748466603540045130".to_string())), + value_of::(native, Box::new("3459661102222301807083870307127272890283709299202626530836335779816726101522661683404130556379097384249447658110805".to_string())), + value_of::(native, Box::new("742483168411032072323733249644347333168432665415341249073150659015707795549260947228694495111018381111866512337576".to_string())), + value_of::(native, Box::new("1662231279858095762833829698537304807741442669992646287950513237989158777254081548205552083108208170765474149568658".to_string())), + value_of::(native, Box::new("1668238650112823419388205992952852912407572045257706138925379268508860023191233729074751042562151098884528280913356".to_string())), + value_of::(native, Box::new("369162719928976119195087327055926326601627748362769544198813069133429557026740823593067700396825489145575282378487".to_string())), + value_of::(native, Box::new("2164195715141237148945939585099633032390257748382945597506236650132835917087090097395995817229686247227784224263055".to_string())), + ]; + self.g1_eval_polynomial(native, true, coeffs, x) + } + pub fn g1_isogeny_x_denominator>( + &mut self, + native: &mut B, + x: &Element, + ) -> Element { + let coeffs = vec![ + value_of::(native, Box::new("1353092447850172218905095041059784486169131709710991428415161466575141675351394082965234118340787683181925558786844".to_string())), + value_of::(native, Box::new("2822220997908397120956501031591772354860004534930174057793539372552395729721474912921980407622851861692773516917759".to_string())), + value_of::(native, Box::new("1717937747208385987946072944131378949849282930538642983149296304709633281382731764122371874602115081850953846504985".to_string())), + value_of::(native, Box::new("501624051089734157816582944025690868317536915684467868346388760435016044027032505306995281054569109955275640941784".to_string())), + value_of::(native, Box::new("3025903087998593826923738290305187197829899948335370692927241015584233559365859980023579293766193297662657497834014".to_string())), + value_of::(native, Box::new("2224140216975189437834161136818943039444741035168992629437640302964164227138031844090123490881551522278632040105125".to_string())), + value_of::(native, Box::new("1146414465848284837484508420047674663876992808692209238763293935905506532411661921697047880549716175045414621825594".to_string())), + value_of::(native, Box::new("3179090966864399634396993677377903383656908036827452986467581478509513058347781039562481806409014718357094150199902".to_string())), + value_of::(native, Box::new("1549317016540628014674302140786462938410429359529923207442151939696344988707002602944342203885692366490121021806145".to_string())), + value_of::(native, Box::new("1442797143427491432630626390066422021593505165588630398337491100088557278058060064930663878153124164818522816175370".to_string())), + ]; + self.g1_eval_polynomial(native, true, coeffs, x) + } + pub fn g1_isogeny_y_numerator>( + &mut self, + native: &mut B, + x: &Element, + y: &Element, + ) -> Element { + let coeffs = vec![ + value_of::(native, Box::new("1393399195776646641963150658816615410692049723305861307490980409834842911816308830479576739332720113414154429643571".to_string())), + value_of::(native, Box::new("2968610969752762946134106091152102846225411740689724909058016729455736597929366401532929068084731548131227395540630".to_string())), + value_of::(native, Box::new("122933100683284845219599644396874530871261396084070222155796123161881094323788483360414289333111221370374027338230".to_string())), + value_of::(native, Box::new("303251954782077855462083823228569901064301365507057490567314302006681283228886645653148231378803311079384246777035".to_string())), + value_of::(native, Box::new("1353972356724735644398279028378555627591260676383150667237975415318226973994509601413730187583692624416197017403099".to_string())), + value_of::(native, Box::new("3443977503653895028417260979421240655844034880950251104724609885224259484262346958661845148165419691583810082940400".to_string())), + value_of::(native, Box::new("718493410301850496156792713845282235942975872282052335612908458061560958159410402177452633054233549648465863759602".to_string())), + value_of::(native, Box::new("1466864076415884313141727877156167508644960317046160398342634861648153052436926062434809922037623519108138661903145".to_string())), + value_of::(native, Box::new("1536886493137106337339531461344158973554574987550750910027365237255347020572858445054025958480906372033954157667719".to_string())), + value_of::(native, Box::new("2171468288973248519912068884667133903101171670397991979582205855298465414047741472281361964966463442016062407908400".to_string())), + value_of::(native, Box::new("3915937073730221072189646057898966011292434045388986394373682715266664498392389619761133407846638689998746172899634".to_string())), + value_of::(native, Box::new("3802409194827407598156407709510350851173404795262202653149767739163117554648574333789388883640862266596657730112910".to_string())), + value_of::(native, Box::new("1707589313757812493102695021134258021969283151093981498394095062397393499601961942449581422761005023512037430861560".to_string())), + value_of::(native, Box::new("349697005987545415860583335313370109325490073856352967581197273584891698473628451945217286148025358795756956811571".to_string())), + value_of::(native, Box::new("885704436476567581377743161796735879083481447641210566405057346859953524538988296201011389016649354976986251207243".to_string())), + value_of::(native, Box::new("3370924952219000111210625390420697640496067348723987858345031683392215988129398381698161406651860675722373763741188".to_string())), + ]; + let dst = self.g1_eval_polynomial(native, false, coeffs, x); + self.curve_f.mul(native, &dst, y) + } + pub fn g1_isogeny_x_numerator>( + &mut self, + native: &mut B, + x: &Element, + ) -> Element { + let coeffs = vec![ + value_of::(native, Box::new("2712959285290305970661081772124144179193819192423276218370281158706191519995889425075952244140278856085036081760695".to_string())), + value_of::(native, Box::new("3564859427549639835253027846704205725951033235539816243131874237388832081954622352624080767121604606753339903542203".to_string())), + value_of::(native, Box::new("2051387046688339481714726479723076305756384619135044672831882917686431912682625619320120082313093891743187631791280".to_string())), + value_of::(native, Box::new("3612713941521031012780325893181011392520079402153354595775735142359240110423346445050803899623018402874731133626465".to_string())), + value_of::(native, Box::new("2247053637822768981792833880270996398470828564809439728372634811976089874056583714987807553397615562273407692740057".to_string())), + value_of::(native, Box::new("3415427104483187489859740871640064348492611444552862448295571438270821994900526625562705192993481400731539293415811".to_string())), + value_of::(native, Box::new("2067521456483432583860405634125513059912765526223015704616050604591207046392807563217109432457129564962571408764292".to_string())), + value_of::(native, Box::new("3650721292069012982822225637849018828271936405382082649291891245623305084633066170122780668657208923883092359301262".to_string())), + value_of::(native, Box::new("1239271775787030039269460763652455868148971086016832054354147730155061349388626624328773377658494412538595239256855".to_string())), + value_of::(native, Box::new("3479374185711034293956731583912244564891370843071137483962415222733470401948838363051960066766720884717833231600798".to_string())), + value_of::(native, Box::new("2492756312273161536685660027440158956721981129429869601638362407515627529461742974364729223659746272460004902959995".to_string())), + value_of::(native, Box::new("1058488477413994682556770863004536636444795456512795473806825292198091015005841418695586811009326456605062948114985".to_string())), + ]; + self.g1_eval_polynomial(native, false, coeffs, x) + } + pub fn g1_eval_polynomial>( + &mut self, + native: &mut B, + monic: bool, + coefficients: Vec>, + x: &Element, + ) -> Element { + let mut dst = coefficients[coefficients.len() - 1].my_clone(); + if monic { + dst = self.curve_f.add(native, &dst, x); + } + for i in (0..coefficients.len() - 1).rev() { + dst = self.curve_f.mul(native, &dst, x); + dst = self.curve_f.add(native, &dst, &coefficients[i]); + } + dst + } + pub fn map_to_g1>( + &mut self, + native: &mut B, + in0: &Element, + in1: &Element, + ) -> G1Affine { + let out0: G1Affine = self.map_to_curve1(native, in0); + let out1 = self.map_to_curve1(native, in1); + let out = self.add(native, &out0, &out1); + let new_out = self.g1_isogeny(native, &out); + self.clear_cofactor(native, &new_out) + } + pub fn mul_windowed>( + &mut self, + native: &mut B, + q: &G1Affine, + s: BigInt, + ) -> G1Affine { + let double_q = self.double(native, q); + let triple_q = self.add(native, &double_q, q); + let ops = vec![q.clone(), double_q, triple_q]; + + let b = s.to_bytes_be(); + let b = &b.1[1..]; + let mut res = ops[2].clone(); + + res = self.double(native, &res); + res = self.double(native, &res); + res = self.add(native, &res, &ops[0]); + + res = self.double(native, &res); + res = self.double(native, &res); + + res = self.double(native, &res); + res = self.double(native, &res); + res = self.add(native, &res, &ops[1]); + + for w in b { + let mut mask = 0xc0; + for j in 0..4 { + res = self.double(native, &res); + res = self.double(native, &res); + let c = (w & mask) >> (6 - 2 * j); + if c != 0 { + res = self.add(native, &res, &ops[(c - 1) as usize]); + } + mask >>= 2; + } + } + res + } + pub fn clear_cofactor>( + &mut self, + native: &mut B, + p: &G1Affine, + ) -> G1Affine { + let x_big = BigInt::from_str("15132376222941642752").expect("Invalid string for BigInt"); + + let res = self.mul_windowed(native, p, x_big.clone()); + self.add(native, &res, p) + } + pub fn map_to_curve1>( + &mut self, + native: &mut B, + in0: &Element, + ) -> G1Affine { + let a = value_of::(native, Box::new("12190336318893619529228877361869031420615612348429846051986726275283378313155663745811710833465465981901188123677".to_string())); + let b = value_of::(native, Box::new("2906670324641927570491258158026293881577086121416628140204402091718288198173574630967936031029026176254968826637280".to_string())); + + //tv1.Square(u) + let tv1 = self.curve_f.mul(native, in0, in0); + + //g1MulByZ(&tv1, &tv1) + let tv1_mul_z = self.curve_f.add(native, &tv1, &tv1); + let tv1_mul_z = self.curve_f.add(native, &tv1_mul_z, &tv1_mul_z); + let tv1_mul_z = self.curve_f.add(native, &tv1_mul_z, &tv1); + let tv1_mul_z = self.curve_f.add(native, &tv1_mul_z, &tv1_mul_z); + let tv1_mul_z = self.curve_f.add(native, &tv1_mul_z, &tv1); + + //tv2.Square(&tv1) + let tv2 = self.curve_f.mul(native, &tv1_mul_z, &tv1_mul_z); + //tv2.Add(&tv2, &tv1) + let tv2 = self.curve_f.add(native, &tv2, &tv1_mul_z); + + let tv4 = self.curve_f.one_const.clone(); + let tv3 = self.curve_f.add(native, &tv2, &tv4); + let tv3 = self.curve_f.mul(native, &tv3, &b); + + let a_neg = self.curve_f.neg(native, &a); + //tv2.Neg(&tv2) + tv2.Mul(&tv2, &sswuIsoCurveCoeffA) + let tv2 = self.curve_f.mul(native, &a_neg, &tv2); + + //tv4.Mul(&tv4, &sswuIsoCurveCoeffA), since they are constant, we skip the mul and get the res value directly + let tv4 = value_of::(native, Box::new("134093699507829814821517650980559345626771735832728306571853989028117161444712301203928819168120125800913069360447".to_string())); + + let tv2_zero = self.curve_f.is_zero(native, &tv2); + + //tv4.Select(int(tv2NZero), &tv2, &tv4) + let tv4 = self.curve_f.select(native, tv2_zero, &tv4, &tv2); + + let tv3_div_tv4 = self.curve_f.div(native, &tv3, &tv4); + + //tv2 = (tv3^2 + tv4^2*a) * tv3 + tv4^3*b + //tv6 = tv4^3 + //need sqrt(tv2/tv6) = sqrt( + //tv3^3 + tv3*tv4^2*a + tv4^3*b)/tv4^3 = tv3_div^3 + tv3_div*a + b) + //) + + //tv3_div^2 + let tv3_div_tv4_sq = self.curve_f.mul(native, &tv3_div_tv4, &tv3_div_tv4); + //tv3_div^3 + let tv3_div_tv4_cub = self.curve_f.mul(native, &tv3_div_tv4, &tv3_div_tv4_sq); + //tv3_div * a + let tv3_div_tv4_a = self.curve_f.mul(native, &a, &tv3_div_tv4); + //tv3_div^3 + tv3_div*a + let ratio_tmp = self.curve_f.add(native, &tv3_div_tv4_cub, &tv3_div_tv4_a); + //ratio = tv3_div^3 + tv3_div*a + b + let y_sq = self.curve_f.add(native, &ratio_tmp, &b); + + //if ratio has square root, then y = sqrt(ratio), otherwise, y = new_y = sqrt(Z * ratio) * tv1 * u + //here, we calculate new_y^2 = Z * ratio * tv1^2 * u^2, here tv1 = u^2 * Z, so we get new_y^2 = ratio * tv1^3 + + //x = tv1 * tv3 + let x1 = self.curve_f.mul(native, &tv1_mul_z, &tv3_div_tv4); + + //tv1^2 + let tv1_mul_z_sq = self.curve_f.mul(native, &tv1_mul_z, &tv1_mul_z); + //tv1^3 + let tv1_mul_z_cub = self.curve_f.mul(native, &tv1_mul_z_sq, &tv1_mul_z); + + //new_y^2 = ratio * tv1^3 + let y1_sq = self.curve_f.mul(native, &tv1_mul_z_cub, &y_sq); + + let inputs = vec![y_sq.clone(), y1_sq.clone(), in0.clone()]; + let output = self + .curve_f + .new_hint(native, "myhint.getsqrtx0x1fqnewhint", 2, inputs); + let is_square = self.curve_f.is_zero(native, &output[0]); // is_square = 0 if y_sq has not square root, 1 otherwise + let res_y = output[1].clone(); + + let res_y_sq = self.curve_f.mul(native, &res_y, &res_y); + + let expected_y_sq = self.curve_f.select(native, is_square, &y1_sq, &res_y_sq); + + self.curve_f + .assert_is_equal(native, &expected_y_sq, &res_y_sq); + + let sgn_in = self.curve_f.get_element_sign(native, in0); + let sgn_y = self.curve_f.get_element_sign(native, &res_y); + + native.assert_is_equal(sgn_in, sgn_y); + + let out_b0 = self.curve_f.select(native, is_square, &x1, &tv3_div_tv4); + let out_b1 = res_y.my_clone(); + G1Affine { + x: out_b0, + y: out_b1, + } + } +} + +declare_circuit!(G1AddCircuit { + p: [[Variable; 48]; 2], + q: [[Variable; 48]; 2], + r: [[Variable; 48]; 2], +}); + +impl Define for G1AddCircuit { + fn define>(&self, builder: &mut Builder) { + let mut g1 = G1::new(builder); + let p1_g1 = G1Affine::from_vars(self.p[0].to_vec(), self.p[1].to_vec()); + let p2_g1 = G1Affine::from_vars(self.q[0].to_vec(), self.q[1].to_vec()); + let r_g1 = G1Affine::from_vars(self.r[0].to_vec(), self.r[1].to_vec()); + let mut r = g1.add(builder, &p1_g1, &p2_g1); + for _ in 0..16 { + r = g1.add(builder, &r, &p2_g1); + } + g1.curve_f.assert_is_equal(builder, &r.x, &r_g1.x); + g1.curve_f.assert_is_equal(builder, &r.y, &r_g1.y); + g1.curve_f.check_mul(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + } +} + +declare_circuit!(G1UncompressCircuit { + x: [Variable; 48], + y: [[Variable; 48]; 2], +}); + +impl Define for G1UncompressCircuit { + fn define>(&self, builder: &mut Builder) { + let mut g1 = G1::new(builder); + let public_key = g1.uncompressed(builder, &self.x); + let expected_g1 = G1Affine::from_vars(self.y[0].to_vec(), self.y[1].to_vec()); + g1.curve_f + .assert_is_equal(builder, &public_key.x, &expected_g1.x); + g1.curve_f + .assert_is_equal(builder, &public_key.y, &expected_g1.y); + g1.curve_f.check_mul(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + } +} + +declare_circuit!(HashToG1Circuit { + msg: [Variable; 32], + out: [[Variable; 48]; 2], +}); + +impl Define for HashToG1Circuit { + fn define>(&self, builder: &mut Builder) { + let mut g1 = G1::new(builder); + let (hm0, hm1) = g1.hash_to_fp(builder, &self.msg); + let res = g1.map_to_g1(builder, &hm0, &hm1); + let target_out = G1Affine::from_vars(self.out[0].to_vec(), self.out[1].to_vec()); + g1.assert_is_equal(builder, &res, &target_out); + g1.curve_f.check_mul(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + } +} + +#[cfg(test)] +mod tests { + use super::G1AddCircuit; + use super::G1UncompressCircuit; + // use super::MapToG1Circuit; + use super::HashToG1Circuit; + use crate::utils::register_hint; + use expander_compiler::frontend::*; + use expander_compiler::{ + compile::CompileOptions, + frontend::{compile, HintRegistry, M31}, + }; + use extra::debug_eval; + use num_bigint::BigInt; + use num_traits::Num; + + #[test] + fn test_g1_add() { + compile(&G1AddCircuit::default(), CompileOptions::default()).unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = G1AddCircuit:: { + p: [[M31::from(0); 48]; 2], + q: [[M31::from(0); 48]; 2], + r: [[M31::from(0); 48]; 2], + }; + let p1_x_bytes = [ + 169, 204, 143, 202, 195, 182, 32, 187, 150, 46, 27, 88, 137, 82, 209, 11, 255, 228, + 147, 72, 218, 149, 56, 139, 243, 28, 49, 146, 210, 5, 238, 232, 111, 204, 78, 170, 83, + 191, 222, 173, 137, 165, 150, 240, 62, 27, 213, 8, + ]; + let p1_y_bytes = [ + 85, 56, 238, 125, 65, 131, 108, 201, 186, 2, 96, 151, 226, 80, 22, 2, 111, 141, 203, + 67, 50, 147, 209, 102, 238, 82, 12, 96, 172, 239, 2, 177, 184, 146, 208, 150, 63, 214, + 239, 198, 101, 74, 169, 226, 148, 53, 104, 1, + ]; + let p2_x_bytes = [ + 108, 4, 52, 16, 255, 115, 116, 198, 234, 60, 202, 181, 169, 240, 221, 33, 38, 178, 114, + 195, 169, 16, 147, 33, 62, 116, 10, 191, 25, 163, 79, 192, 140, 43, 109, 235, 157, 42, + 15, 48, 115, 213, 48, 51, 19, 165, 178, 17, + ]; + let p2_y_bytes = [ + 130, 146, 65, 1, 211, 117, 217, 145, 69, 140, 76, 106, 43, 160, 192, 247, 96, 225, 2, + 72, 219, 238, 254, 202, 9, 210, 253, 111, 73, 49, 26, 145, 68, 161, 64, 101, 238, 0, + 236, 128, 164, 92, 95, 30, 143, 178, 6, 20, + ]; + let res_x_bytes = [ + 148, 92, 212, 64, 35, 246, 218, 14, 150, 169, 177, 191, 61, 6, 4, 120, 60, 253, 36, + 139, 95, 95, 14, 122, 89, 3, 62, 198, 100, 50, 114, 221, 144, 187, 29, 15, 203, 89, + 220, 29, 120, 25, 153, 169, 184, 184, 133, 16, + ]; + let res_y_bytes = [ + 41, 226, 254, 238, 50, 145, 74, 128, 160, 125, 237, 161, 93, 66, 241, 104, 218, 230, + 154, 134, 24, 204, 225, 220, 175, 115, 243, 57, 238, 157, 161, 175, 213, 34, 145, 106, + 226, 230, 19, 110, 196, 196, 229, 104, 152, 64, 12, 6, + ]; + + for i in 0..48 { + assignment.p[0][i] = M31::from(p1_x_bytes[i]); + assignment.p[1][i] = M31::from(p1_y_bytes[i]); + assignment.q[0][i] = M31::from(p2_x_bytes[i]); + assignment.q[1][i] = M31::from(p2_y_bytes[i]); + assignment.r[0][i] = M31::from(res_x_bytes[i]); + assignment.r[1][i] = M31::from(res_y_bytes[i]); + } + + debug_eval(&G1AddCircuit::default(), &assignment, hint_registry); + } + + #[test] + fn test_uncompress_g1() { + // compile(&G1UncompressCircuit::default(), CompileOptions::default()).unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = G1UncompressCircuit:: { + x: [M31::default(); 48], + y: [[M31::default(); 48]; 2], + }; + let x_bigint = BigInt::from_str_radix("a637bd4aefa20593ff82bdf832db2a98ca60c87796bca1d04a5a0206d52b4ede0e906d903360e04b69f8daec631f79fe", 16).unwrap(); + + let x_bytes = x_bigint.to_bytes_be(); + + let y_a0_bigint = BigInt::from_str_radix("956996561804650125715590823042978408716123343953697897618645235063950952926609558156980737775438019700668816652798", 10).unwrap(); + let y_a1_bigint = BigInt::from_str_radix("3556009343530533802204184826723274316816769528634825602353881354158551671080148026501040863742187196667680827782849", 10).unwrap(); + + let y_a0_bytes = y_a0_bigint.to_bytes_le(); + let y_a1_bytes = y_a1_bigint.to_bytes_le(); + + for i in 0..48 { + assignment.x[i] = M31::from(x_bytes.1[i] as u32); + assignment.y[0][i] = M31::from(y_a0_bytes.1[i] as u32); + assignment.y[1][i] = M31::from(y_a1_bytes.1[i] as u32); + } + + debug_eval(&G1UncompressCircuit::default(), &assignment, hint_registry); + } + + #[test] + fn test_hash_to_g1() { + // compile(&HashToG2Circuit::default(), CompileOptions::default()).unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = HashToG1Circuit:: { + msg: [M31::from(0); 32], + out: [[M31::from(0); 48]; 2], + }; + let x_bigint = BigInt::from_str_radix( + "8c944f8caa55d007728a2fc6e7ff3068dde103ed63fb399c59c24f1f826de4c7", + 16, + ) + .unwrap(); + + let x_bytes = x_bigint.to_bytes_be(); + + let y_a0_bigint = BigInt::from_str_radix("931508203449116360366484402715781657513658072828297647050637028707500425620237136600612884240951972079295402518955", 10).unwrap(); + let y_a1_bigint = BigInt::from_str_radix("519166679736366508158130784988422711323587004159773257823344793142122588338441738530109373213103052261922442631575", 10).unwrap(); + let y_a0_bytes = y_a0_bigint.to_bytes_le(); + let y_a1_bytes = y_a1_bigint.to_bytes_le(); + + for i in 0..32 { + assignment.msg[i] = M31::from(x_bytes.1[i] as u32); + } + for i in 0..48 { + assignment.out[0][i] = M31::from(y_a0_bytes.1[i] as u32); + assignment.out[1][i] = M31::from(y_a1_bytes.1[i] as u32); + } + + debug_eval(&HashToG1Circuit::default(), &assignment, hint_registry); + } } diff --git a/circuit-std-rs/src/gnark/emulated/sw_bls12381/g2.rs b/circuit-std-rs/src/gnark/emulated/sw_bls12381/g2.rs index 96e2074c..87045b60 100644 --- a/circuit-std-rs/src/gnark/emulated/sw_bls12381/g2.rs +++ b/circuit-std-rs/src/gnark/emulated/sw_bls12381/g2.rs @@ -1,7 +1,17 @@ +use crate::gnark::element::*; use crate::gnark::emparam::Bls12381Fp; use crate::gnark::emulated::field_bls12381::e2::Ext2; use crate::gnark::emulated::field_bls12381::e2::GE2; -use expander_compiler::frontend::*; +use crate::sha256::m31_utils::*; +use crate::utils::simple_select; +use expander_compiler::declare_circuit; +use expander_compiler::frontend::{Config, Define, M31Config, RootAPI, Variable}; +use num_bigint::BigInt; +use std::str::FromStr; + +const M_COMPRESSED_SMALLEST: u8 = 0b100 << 5; +const M_COMPRESSED_LARGEST: u8 = 0b101 << 5; + #[derive(Default, Clone)] pub struct G2AffP { pub x: GE2, @@ -25,20 +35,6 @@ impl G2AffP { } } -pub struct G2 { - pub curve_f: Ext2, -} - -impl G2 { - pub fn new>(native: &mut B) -> Self { - let curve_f = Ext2::new(native); - Self { curve_f } - } - pub fn neg>(&mut self, native: &mut B, p: &G2AffP) -> G2AffP { - let yr = self.curve_f.neg(native, &p.y); - G2AffP::new(p.x.my_clone(), yr) - } -} #[derive(Default)] pub struct LineEvaluation { pub r0: GE2, @@ -65,3 +61,706 @@ pub struct G2Affine { pub p: G2AffP, pub lines: LineEvaluations, } + +pub struct G2 { + pub ext2: Ext2, + pub u1: Element, + pub v: GE2, +} + +impl G2 { + pub fn new>(native: &mut B) -> Self { + let curve_f = Ext2::new(native); + let u1 = value_of::(native, Box::new("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437".to_string())); + let v0 = value_of::(native, Box::new("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530".to_string())); + let v1 = value_of::(native, Box::new("1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257".to_string())); + let v = GE2::from_vars(v0.limbs, v1.limbs); + Self { + ext2: curve_f, + u1, + v, + } + } + pub fn neg>(&mut self, native: &mut B, p: &G2AffP) -> G2AffP { + let yr = self.ext2.neg(native, &p.y); + G2AffP::new(p.x.my_clone(), yr) + } + pub fn copy_g2_aff_p>( + &mut self, + native: &mut B, + q: &G2AffP, + ) -> G2AffP { + let copy_q_acc_x = self.ext2.copy(native, &q.x); + let copy_q_acc_y = self.ext2.copy(native, &q.y); + G2AffP { + x: copy_q_acc_x, + y: copy_q_acc_y, + } + } + pub fn g2_double>(&mut self, native: &mut B, p: &G2AffP) -> G2AffP { + let xx3a = self.ext2.square(native, &p.x); + let xx3a = self + .ext2 + .mul_by_const_element(native, &xx3a, &BigInt::from(3)); + let y2 = self.ext2.double(native, &p.y); + let λ = self.ext2.div(native, &xx3a, &y2); + + let x2 = self.ext2.double(native, &p.x); + let λλ = self.ext2.square(native, &λ); + let xr = self.ext2.sub(native, &λλ, &x2); + + let pxrx = self.ext2.sub(native, &p.x, &xr); + let λpxrx = self.ext2.mul(native, &λ, &pxrx); + let yr = self.ext2.sub(native, &λpxrx, &p.y); + + G2AffP::new(xr, yr) + } + pub fn assert_is_equal>( + &mut self, + native: &mut B, + p: &G2AffP, + q: &G2AffP, + ) { + self.ext2.assert_isequal(native, &p.x, &q.x); + self.ext2.assert_isequal(native, &p.y, &q.y); + } + pub fn g2_add>( + &mut self, + native: &mut B, + p: &G2AffP, + q: &G2AffP, + ) -> G2AffP { + let qypy = self.ext2.sub(native, &q.y, &p.y); + let qxpx = self.ext2.sub(native, &q.x, &p.x); + let λ = self.ext2.div(native, &qypy, &qxpx); + + let λλ = self.ext2.square(native, &λ); + let qxpx = self.ext2.add(native, &p.x, &q.x); + let xr = self.ext2.sub(native, &λλ, &qxpx); + + let pxrx = self.ext2.sub(native, &p.x, &xr); + let λpxrx = self.ext2.mul(native, &λ, &pxrx); + let yr = self.ext2.sub(native, &λpxrx, &p.y); + + G2AffP::new(xr, yr) + } + pub fn psi>(&mut self, native: &mut B, q: &G2AffP) -> G2AffP { + let x = self.ext2.mul_by_element(native, &q.x, &self.u1); + let y = self.ext2.conjugate(native, &q.y); + let y = self.ext2.mul(native, &y, &self.v); + G2AffP::new(GE2::from_vars(x.a1.limbs, x.a0.limbs), y) + } + pub fn mul_windowed>( + &mut self, + native: &mut B, + q: &G2AffP, + s: BigInt, + ) -> G2AffP { + let mut ops = [ + self.copy_g2_aff_p(native, q), + self.copy_g2_aff_p(native, q), + self.copy_g2_aff_p(native, q), + ]; + ops[1] = self.g2_double(native, &ops[1]); + ops[2] = self.g2_add(native, &ops[0], &ops[1]); + let b = s.to_bytes_be(); + let b = &b.1[1..]; + let mut res = self.copy_g2_aff_p(native, &ops[2]); + + res = self.g2_double(native, &res); + res = self.g2_double(native, &res); + res = self.g2_add(native, &res, &ops[0]); + + res = self.g2_double(native, &res); + res = self.g2_double(native, &res); + + res = self.g2_double(native, &res); + res = self.g2_double(native, &res); + res = self.g2_add(native, &res, &ops[1]); + // let mut copy_res = self.copy_g2_aff_p(native, &res); + for w in b { + let mut mask = 0xc0; + for j in 0..4 { + res = self.g2_double(native, &res); + res = self.g2_double(native, &res); + let c = (w & mask) >> (6 - 2 * j); + if c != 0 { + res = self.g2_add(native, &res, &ops[(c - 1) as usize]); + } + mask >>= 2; + } + } + res + } + pub fn clear_cofactor>( + &mut self, + native: &mut B, + p: &G2AffP, + ) -> G2AffP { + let p_neg = self.neg(native, p); + let x_big = BigInt::from_str("15132376222941642752").expect("Invalid string for BigInt"); + + let xg_neg = self.mul_windowed(native, p, x_big.clone()); + let xg = self.neg(native, &xg_neg); + + let xxg = self.mul_windowed(native, &xg, x_big.clone()); + let xxg = self.neg(native, &xxg); + + let mut res = self.g2_add(native, &xxg, &xg_neg); + res = self.g2_add(native, &res, &p_neg); + + let mut t = self.g2_add(native, &xg, &p_neg); + t = self.psi(native, &t); + + res = self.g2_add(native, &res, &t); + + let t_double = self.g2_double(native, p); + + let third_root_one_g1 = value_of::(native, Box::new("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436".to_string())); + + let mut t_double_mul = G2AffP::new(t_double.x.my_clone(), t_double.y.my_clone()); + t_double_mul.x = self + .ext2 + .mul_by_element(native, &t_double_mul.x, &third_root_one_g1); + t_double_mul = self.neg(native, &t_double_mul); + + self.g2_add(native, &res, &t_double_mul) + } + pub fn map_to_curve2>(&mut self, native: &mut B, in0: &GE2) -> G2AffP { + let a = GE2::from_vars( + value_of::(native, Box::new(0)).limbs, + value_of::(native, Box::new(240)).limbs, + ); + let b = GE2::from_vars( + value_of::(native, Box::new(1012)).limbs, + value_of::(native, Box::new(1012)).limbs, + ); + + let xi = GE2::from_vars( + value_of::(native, Box::new(-2i32)).limbs, + value_of::(native, Box::new(-1i32)).limbs, + ); + + let t_sq = self.ext2.square(native, in0); + let xi_t_sq = self.ext2.mul(native, &t_sq, &xi); + + let xi_2_t_4 = self.ext2.square(native, &xi_t_sq); + let num_den_common = self.ext2.add(native, &xi_2_t_4, &xi_t_sq); + + let a_neg = self.ext2.neg(native, &a); + let x0_den = self.ext2.mul(native, &a_neg, &num_den_common); + + let x1_den = GE2::from_vars( + value_of::(native, Box::new(240)).limbs, value_of::(native, Box::new("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436".to_string())).limbs, + ); + + let exception = self.ext2.is_zero(native, &x0_den); + + let one = self.ext2.one().clone(); + let num_den_common = self.ext2.add(native, &num_den_common, &one); + let x0_num = self.ext2.mul(native, &num_den_common, &b); + + let denom = self.ext2.select(native, exception, &x1_den, &x0_den); + + let x0 = self.ext2.div(native, &x0_num, &denom); + + let x0_sq = self.ext2.square(native, &x0); + let x0_cub = self.ext2.mul(native, &x0, &x0_sq); + let x0_a = self.ext2.mul(native, &a, &x0); + let g_x0_tmp = self.ext2.add(native, &x0_cub, &x0_a); + let g_x0 = self.ext2.add(native, &g_x0_tmp, &b); + + let x1 = self.ext2.mul(native, &xi_t_sq, &x0); + + let xi_3_t_6_tmp = self.ext2.mul(native, &xi_t_sq, &xi_t_sq); + let xi_3_t_6 = self.ext2.mul(native, &xi_3_t_6_tmp, &xi_t_sq); + + let g_x1 = self.ext2.mul(native, &xi_3_t_6, &g_x0); + + let inputs = vec![ + g_x0.a0.my_clone(), + g_x0.a1.my_clone(), + g_x1.a0.my_clone(), + g_x1.a1.my_clone(), + in0.a0.my_clone(), + in0.a1.my_clone(), + ]; + let output = self + .ext2 + .curve_f + .new_hint(native, "myhint.getsqrtx0x1fq2newhint", 3, inputs); + let is_square = self.ext2.curve_f.is_zero(native, &output[0]); // is_square = 0 if g_x0 has not square root, 1 otherwise + let y = GE2 { + a0: output[1].my_clone(), + a1: output[2].my_clone(), + }; + + let y_sq = self.ext2.square(native, &y); + let expected = self.ext2.select(native, is_square, &g_x1, &g_x0); + + self.ext2.assert_isequal(native, &expected, &y_sq); + + let in_x0_zero = self.ext2.curve_f.is_zero(native, &in0.a0); + let y_x0_zero = self.ext2.curve_f.is_zero(native, &y.a0); + let sgn_in = self.ext2.get_e2_sign(native, in0, in_x0_zero); + let sgn_y = self.ext2.get_e2_sign(native, &y, y_x0_zero); + + native.assert_is_equal(sgn_in, sgn_y); + + let out_b0 = self.ext2.select(native, is_square, &x1, &x0); + let out_b1 = y.my_clone(); + G2AffP { + x: out_b0, + y: out_b1, + } + } + pub fn g2_eval_polynomial>( + &mut self, + native: &mut B, + monic: bool, + coefficients: Vec, + x: &GE2, + ) -> GE2 { + let mut dst = coefficients[coefficients.len() - 1].my_clone(); + if monic { + dst = self.ext2.add(native, &dst, x); + } + for i in (0..coefficients.len() - 1).rev() { + dst = self.ext2.mul(native, &dst, x); + dst = self.ext2.add(native, &dst, &coefficients[i]); + } + dst + } + pub fn g2_isogeny_x_numerator>( + &mut self, + native: &mut B, + x: &GE2, + ) -> GE2 { + let coeff0 = GE2::from_vars( + value_of::(native, Box::new("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235542".to_string())).limbs, + value_of::(native, Box::new("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235542".to_string())).limbs, + ); + let coeff1 = GE2::from_vars( + value_of::(native, Box::new(0)).limbs, + value_of::(native, Box::new("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706522".to_string())).limbs, + ); + let coeff2 = GE2::from_vars( + value_of::(native, Box::new("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706526".to_string())).limbs, + value_of::(native, Box::new("1334136518407222464472596608578634718852294273313002628444019378708010550163612621480895876376338554679298090853261".to_string())).limbs, + ); + let coeff3 = GE2::from_vars( + value_of::(native, Box::new("3557697382419259905260257622876359250272784728834673675850718343221361467102966990615722337003569479144794908942033".to_string())).limbs, + value_of::(native, Box::new(0)).limbs, + ); + self.g2_eval_polynomial(native, false, vec![coeff0, coeff1, coeff2, coeff3], x) + } + pub fn g2_isogeny_y_numerator>( + &mut self, + native: &mut B, + x: &GE2, + y: &GE2, + ) -> GE2 { + let coeff0 = GE2::from_vars( + value_of::(native, Box::new("3261222600550988246488569487636662646083386001431784202863158481286248011511053074731078808919938689216061999863558".to_string())).limbs, + value_of::(native, Box::new("3261222600550988246488569487636662646083386001431784202863158481286248011511053074731078808919938689216061999863558".to_string())).limbs, + ); + let coeff1 = GE2::from_vars( + value_of::(native, Box::new(0)).limbs, + value_of::(native, Box::new("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235518".to_string())).limbs, + ); + let coeff2 = GE2::from_vars( + value_of::(native, Box::new("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706524".to_string())).limbs, + value_of::(native, Box::new("1334136518407222464472596608578634718852294273313002628444019378708010550163612621480895876376338554679298090853263".to_string())).limbs, + ); + let coeff3 = GE2::from_vars( + value_of::(native, Box::new("2816510427748580758331037284777117739799287910327449993381818688383577828123182200904113516794492504322962636245776".to_string())).limbs, + value_of::(native, Box::new(0)).limbs, + ); + let dst = self.g2_eval_polynomial(native, false, vec![coeff0, coeff1, coeff2, coeff3], x); + self.ext2.mul(native, &dst, y) + } + pub fn g2_isogeny_x_denominator>( + &mut self, + native: &mut B, + x: &GE2, + ) -> GE2 { + let coeff0 = GE2::from_vars( + value_of::(native, Box::new(0)).limbs, + value_of::(native, Box::new(-72)).limbs, + ); + let coeff1 = GE2::from_vars( + value_of::(native, Box::new(12)).limbs, + value_of::(native, Box::new(-12)).limbs, + ); + self.g2_eval_polynomial(native, true, vec![coeff0, coeff1], x) + } + pub fn g2_isogeny_y_denominator>( + &mut self, + native: &mut B, + x: &GE2, + ) -> GE2 { + let coeff0 = GE2::from_vars( + value_of::(native, Box::new(-432)).limbs, + value_of::(native, Box::new(-432)).limbs, + ); + let coeff1 = GE2::from_vars( + value_of::(native, Box::new(0)).limbs, + value_of::(native, Box::new(-216)).limbs, + ); + let coeff2 = GE2::from_vars( + value_of::(native, Box::new(18)).limbs, + value_of::(native, Box::new(-18)).limbs, + ); + self.g2_eval_polynomial(native, true, vec![coeff0, coeff1, coeff2], x) + } + pub fn g2_isogeny>(&mut self, native: &mut B, p: &G2AffP) -> G2AffP { + let mut p = G2AffP { + x: p.x.my_clone(), + y: p.y.my_clone(), + }; + let den1 = self.g2_isogeny_y_denominator(native, &p.x); + let den0 = self.g2_isogeny_x_denominator(native, &p.x); + p.y = self.g2_isogeny_y_numerator(native, &p.x, &p.y); + p.x = self.g2_isogeny_x_numerator(native, &p.x); + + let den0 = self.ext2.inverse(native, &den0); + let den1 = self.ext2.inverse(native, &den1); + + p.x = self.ext2.mul(native, &p.x, &den0); + p.y = self.ext2.mul(native, &p.y, &den1); + p + } + pub fn hash_to_fp>( + &mut self, + native: &mut B, + data: &[Variable], + ) -> (GE2, GE2) { + let u = self.ext2.curve_f.hash_to_fp(native, data, 2 * 2); + ( + GE2::from_vars(u[0].clone().limbs, u[1].clone().limbs), + GE2::from_vars(u[2].clone().limbs, u[3].clone().limbs), + ) + } + pub fn map_to_g2>( + &mut self, + native: &mut B, + in0: &GE2, + in1: &GE2, + ) -> G2AffP { + let out0 = self.map_to_curve2(native, in0); + let out1 = self.map_to_curve2(native, in1); + let out = self.g2_add(native, &out0, &out1); + let new_out = self.g2_isogeny(native, &out); + self.clear_cofactor(native, &new_out) + } + + pub fn uncompressed>( + &mut self, + native: &mut B, + bytes: &[Variable], + ) -> G2AffP { + let mut buf_x = bytes.to_vec(); + let buf0 = to_binary(native, buf_x[0], 8); + let pad = vec![native.constant(0); 5]; + let m_data = from_binary(native, [pad, buf0[5..].to_vec()].concat()); //buf0 & mMask + let buf0_and_non_mask = from_binary(native, buf0[..5].to_vec()); //buf0 & ^mMask + buf_x[0] = buf0_and_non_mask; + + //get p.x + let rev_buf = buf_x.iter().rev().cloned().collect::>(); + let px = GE2::from_vars(rev_buf[0..48].to_vec(), rev_buf[48..].to_vec()); + + //get YSquared + let ysquared = self.ext2.square(native, &px); + let ysquared = self.ext2.mul(native, &ysquared, &px); + let b_curve_coeff = value_of::(native, Box::new(4)); + let b_twist_curve_coeff = + GE2::from_vars(b_curve_coeff.clone().limbs, b_curve_coeff.clone().limbs); + let ysquared = self.ext2.add(native, &ysquared, &b_twist_curve_coeff); + + let inputs = vec![ysquared.a0.clone(), ysquared.a1.clone()]; + let outputs = self + .ext2 + .curve_f + .new_hint(native, "myhint.gete2sqrthint", 3, inputs); + + //is_square should be one + let is_square = outputs[0].clone(); + let one = self.ext2.curve_f.one_const.clone(); + self.ext2.curve_f.assert_is_equal(native, &is_square, &one); + + //get Y + let y = GE2::from_vars(outputs[1].clone().limbs, outputs[2].clone().limbs); + //y^2 = ysquared + let y_squared = self.ext2.square(native, &y); + self.ext2.assert_isequal(native, &y_squared, &ysquared); + + //if y is lexicographically largest + let half_fp = BigInt::from_str("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787").unwrap() / 2; + let half_fp_var = value_of::(native, Box::new(half_fp)); + let is_large_a1 = big_less_than( + native, + Bls12381Fp::bits_per_limb() as usize, + Bls12381Fp::nb_limbs() as usize, + &half_fp_var.limbs, + &y.a1.limbs, + ); + let is_zero_a1 = self.ext2.curve_f.is_zero(native, &y.a1); + let is_large_a0 = big_less_than( + native, + Bls12381Fp::bits_per_limb() as usize, + Bls12381Fp::nb_limbs() as usize, + &half_fp_var.limbs, + &y.a0.limbs, + ); + let is_large = simple_select(native, is_zero_a1, is_large_a0, is_large_a1); + + //if Y > -Y --> check if mData == mCompressedSmallest + //if Y <= -Y --> check if mData == mCompressedLargest + let m_compressed_largest = native.constant(M_COMPRESSED_LARGEST as u32); + let m_compressed_smallest = native.constant(M_COMPRESSED_SMALLEST as u32); + let check_m_data = simple_select( + native, + is_large, + m_compressed_smallest, + m_compressed_largest, + ); + + let check_res = native.sub(m_data, check_m_data); + let neg_flag = native.is_zero(check_res); + + let neg_y = self.ext2.neg(native, &y); + + let y = self.ext2.select(native, neg_flag, &neg_y, &y); + + //TBD: subgroup check, do we need to do that? Since we are pretty sure that the sig bytes are correct, its unmashalling must be on the right curve? + G2AffP { x: px, y } + } +} + +declare_circuit!(G2UncompressCircuit { + x: [Variable; 96], + y: [[[Variable; 48]; 2]; 2], +}); + +impl Define for G2UncompressCircuit { + fn define>(&self, builder: &mut Builder) { + let mut g2 = G2::new(builder); + let g2_res = g2.uncompressed(builder, &self.x); + let expected_g2 = G2AffP::from_vars( + self.y[0][0].to_vec(), + self.y[0][1].to_vec(), + self.y[1][0].to_vec(), + self.y[1][1].to_vec(), + ); + g2.ext2.assert_isequal(builder, &g2_res.x, &expected_g2.x); + g2.ext2.assert_isequal(builder, &g2_res.y, &expected_g2.y); + g2.ext2.curve_f.check_mul(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + } +} + +declare_circuit!(MapToG2Circuit { + in0: [[Variable; 48]; 2], + in1: [[Variable; 48]; 2], + out: [[[Variable; 48]; 2]; 2], +}); + +impl Define for MapToG2Circuit { + fn define>(&self, builder: &mut Builder) { + let mut g2 = G2::new(builder); + let in0 = GE2::from_vars(self.in0[0].to_vec(), self.in0[1].to_vec()); + let in1 = GE2::from_vars(self.in1[0].to_vec(), self.in1[1].to_vec()); + let res = g2.map_to_g2(builder, &in0, &in1); + let target_out = G2AffP { + x: GE2::from_vars(self.out[0][0].to_vec(), self.out[0][1].to_vec()), + y: GE2::from_vars(self.out[1][0].to_vec(), self.out[1][1].to_vec()), + }; + g2.assert_is_equal(builder, &res, &target_out); + g2.ext2.curve_f.check_mul(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + } +} + +declare_circuit!(HashToG2Circuit { + msg: [Variable; 32], + out: [[[Variable; 48]; 2]; 2], +}); + +impl Define for HashToG2Circuit { + fn define>(&self, builder: &mut Builder) { + let mut g2 = G2::new(builder); + let (hm0, hm1) = g2.hash_to_fp(builder, &self.msg); + let res = g2.map_to_g2(builder, &hm0, &hm1); + let target_out = G2AffP { + x: GE2::from_vars(self.out[0][0].to_vec(), self.out[0][1].to_vec()), + y: GE2::from_vars(self.out[1][0].to_vec(), self.out[1][1].to_vec()), + }; + g2.assert_is_equal(builder, &res, &target_out); + g2.ext2.curve_f.check_mul(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + } +} + +#[cfg(test)] +mod tests { + use super::G2UncompressCircuit; + use crate::gnark::emulated::sw_bls12381::g2::*; + use crate::utils::register_hint; + use expander_compiler::frontend::*; + use expander_compiler::frontend::{HintRegistry, M31}; + use extra::debug_eval; + use num_bigint::BigInt; + use num_traits::Num; + + #[test] + fn test_map_to_g2() { + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = MapToG2Circuit:: { + in0: [[M31::from(0); 48]; 2], + in1: [[M31::from(0); 48]; 2], + out: [[[M31::from(0); 48]; 2]; 2], + }; + let p1_x_bytes = [ + 75, 240, 55, 239, 72, 231, 76, 188, 20, 26, 234, 236, 23, 166, 182, 159, 239, 165, 10, + 98, 220, 117, 40, 167, 160, 143, 63, 57, 113, 82, 97, 238, 36, 48, 226, 19, 210, 13, + 216, 163, 51, 199, 31, 228, 211, 18, 125, 25, + ]; + let p1_y_bytes = [ + 161, 161, 201, 159, 90, 241, 214, 89, 177, 71, 235, 130, 168, 37, 237, 255, 26, 105, + 22, 122, 136, 28, 83, 245, 117, 135, 212, 63, 208, 241, 109, 4, 109, 188, 74, 50, 63, + 41, 78, 174, 164, 121, 104, 77, 56, 23, 100, 5, + ]; + let p2_x_bytes = [ + 161, 152, 122, 79, 206, 47, 160, 114, 196, 82, 17, 183, 227, 115, 71, 7, 9, 141, 33, + 224, 127, 254, 158, 109, 69, 225, 184, 146, 239, 137, 146, 138, 224, 79, 56, 100, 184, + 236, 99, 77, 28, 117, 111, 179, 106, 181, 35, 21, + ]; + let p2_y_bytes = [ + 199, 231, 196, 205, 165, 5, 112, 203, 238, 82, 8, 79, 245, 151, 226, 80, 154, 146, 230, + 51, 79, 60, 20, 190, 9, 171, 34, 41, 131, 165, 60, 0, 10, 197, 177, 140, 108, 41, 99, + 113, 151, 51, 253, 219, 105, 227, 25, 24, + ]; + let out0_x_bytes = [ + 215, 186, 167, 113, 176, 255, 84, 123, 163, 0, 104, 202, 139, 197, 29, 119, 253, 35, + 206, 68, 130, 75, 218, 109, 179, 63, 65, 197, 67, 206, 64, 89, 30, 201, 95, 238, 5, 66, + 143, 94, 37, 238, 150, 113, 159, 165, 110, 3, + ]; + let out0_y_bytes = [ + 88, 110, 24, 185, 208, 195, 142, 173, 176, 12, 228, 155, 64, 223, 147, 25, 37, 234, + 200, 3, 123, 119, 193, 221, 234, 253, 199, 190, 120, 135, 32, 215, 32, 118, 55, 230, + 74, 204, 56, 12, 24, 221, 240, 188, 188, 76, 233, 20, + ]; + let out1_x_bytes = [ + 202, 105, 74, 230, 255, 158, 238, 160, 121, 234, 219, 154, 239, 176, 232, 81, 56, 53, + 154, 76, 221, 53, 156, 165, 215, 18, 148, 34, 124, 242, 154, 218, 243, 171, 88, 53, 13, + 182, 39, 84, 254, 161, 96, 192, 154, 242, 71, 15, + ]; + let out1_y_bytes = [ + 66, 124, 60, 101, 29, 246, 150, 109, 233, 119, 212, 23, 132, 79, 170, 0, 178, 98, 151, + 189, 214, 70, 171, 93, 2, 98, 194, 243, 38, 160, 178, 224, 91, 20, 11, 209, 190, 76, + 182, 253, 89, 144, 170, 191, 128, 66, 207, 1, + ]; + + for i in 0..48 { + assignment.in0[0][i] = M31::from(p1_x_bytes[i]); + assignment.in0[1][i] = M31::from(p1_y_bytes[i]); + assignment.in1[0][i] = M31::from(p2_x_bytes[i]); + assignment.in1[1][i] = M31::from(p2_y_bytes[i]); + assignment.out[0][0][i] = M31::from(out0_x_bytes[i]); + assignment.out[0][1][i] = M31::from(out0_y_bytes[i]); + assignment.out[1][0][i] = M31::from(out1_x_bytes[i]); + assignment.out[1][1][i] = M31::from(out1_y_bytes[i]); + } + + debug_eval(&MapToG2Circuit::default(), &assignment, hint_registry); + } + + #[test] + fn test_hash_to_g2() { + // compile_generic(&HashToG2Circuit::default(), CompileOptions::default()).unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = HashToG2Circuit:: { + msg: [M31::from(0); 32], + out: [[[M31::from(0); 48]; 2]; 2], + }; + let msg_bytes = [ + 140, 148, 79, 140, 170, 85, 208, 7, 114, 138, 47, 198, 231, 255, 48, 104, 221, 225, 3, + 237, 99, 251, 57, 156, 89, 194, 79, 31, 130, 109, 228, 200, + ]; + let out0_x_bytes = [ + 215, 186, 167, 113, 176, 255, 84, 123, 163, 0, 104, 202, 139, 197, 29, 119, 253, 35, + 206, 68, 130, 75, 218, 109, 179, 63, 65, 197, 67, 206, 64, 89, 30, 201, 95, 238, 5, 66, + 143, 94, 37, 238, 150, 113, 159, 165, 110, 3, + ]; + let out0_y_bytes = [ + 88, 110, 24, 185, 208, 195, 142, 173, 176, 12, 228, 155, 64, 223, 147, 25, 37, 234, + 200, 3, 123, 119, 193, 221, 234, 253, 199, 190, 120, 135, 32, 215, 32, 118, 55, 230, + 74, 204, 56, 12, 24, 221, 240, 188, 188, 76, 233, 20, + ]; + let out1_x_bytes = [ + 202, 105, 74, 230, 255, 158, 238, 160, 121, 234, 219, 154, 239, 176, 232, 81, 56, 53, + 154, 76, 221, 53, 156, 165, 215, 18, 148, 34, 124, 242, 154, 218, 243, 171, 88, 53, 13, + 182, 39, 84, 254, 161, 96, 192, 154, 242, 71, 15, + ]; + let out1_y_bytes = [ + 66, 124, 60, 101, 29, 246, 150, 109, 233, 119, 212, 23, 132, 79, 170, 0, 178, 98, 151, + 189, 214, 70, 171, 93, 2, 98, 194, 243, 38, 160, 178, 224, 91, 20, 11, 209, 190, 76, + 182, 253, 89, 144, 170, 191, 128, 66, 207, 1, + ]; + for i in 0..32 { + assignment.msg[i] = M31::from(msg_bytes[i]); + } + for i in 0..48 { + assignment.out[0][0][i] = M31::from(out0_x_bytes[i]); + assignment.out[0][1][i] = M31::from(out0_y_bytes[i]); + assignment.out[1][0][i] = M31::from(out1_x_bytes[i]); + assignment.out[1][1][i] = M31::from(out1_y_bytes[i]); + } + + debug_eval(&HashToG2Circuit::default(), &assignment, hint_registry); + } + + #[test] + fn test_uncompress_g2() { + // compile_generic(&G2UncompressCircuit::default(), CompileOptions::default()).unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = G2UncompressCircuit:: { + x: [M31::default(); 96], + y: [[[M31::default(); 48]; 2]; 2], + }; + let x_bigint = BigInt::from_str_radix("aa79bf02bb1633716de959b5ed8ccf7548e6733d7ca11791f1f5d386afb6cebc7cf0339a791bd9187e5346185ace329402b641d106d783e7fe20e5c1cf5b3416590ad45004a0b396f66178511ce724c3df76c2fae61fb682a3ec2dde1ae5a359", 16).unwrap(); + + let x_bytes = x_bigint.to_bytes_be(); + + let y_b0_a0_bigint = BigInt::from_str_radix("417406042303837766676050444382954581819710384023930335899613364000243943316124744931107291428889984115562657456985", 10).unwrap(); + let y_b0_a1_bigint = BigInt::from_str_radix("1612337918776384379710682981548399375489832112491603419994252758241488024847803823620674751718035900645102653944468", 10).unwrap(); + let y_b1_a0_bigint = BigInt::from_str_radix("2138372746384454686692156684769748785619173944336480358459807585988147682623523096063056865298570471165754367761702", 10).unwrap(); + let y_b1_a1_bigint = BigInt::from_str_radix("2515621099638397509480666850964364949449167540660259026336903510150090825582288208580180650995842554224706524936338", 10).unwrap(); + + let y_a0_bytes = y_b0_a0_bigint.to_bytes_le(); + let y_a1_bytes = y_b0_a1_bigint.to_bytes_le(); + let y_b0_bytes = y_b1_a0_bigint.to_bytes_le(); + let y_b1_bytes = y_b1_a1_bigint.to_bytes_le(); + + for i in 0..48 { + assignment.x[i] = M31::from(x_bytes.1[i] as u32); + assignment.x[i + 48] = M31::from(x_bytes.1[i + 48] as u32); + assignment.y[0][0][i] = M31::from(y_a0_bytes.1[i] as u32); + assignment.y[0][1][i] = M31::from(y_a1_bytes.1[i] as u32); + assignment.y[1][0][i] = M31::from(y_b0_bytes.1[i] as u32); + assignment.y[1][1][i] = M31::from(y_b1_bytes.1[i] as u32); + } + + debug_eval(&G2UncompressCircuit::default(), &assignment, hint_registry); + } +} diff --git a/circuit-std-rs/src/gnark/field.rs b/circuit-std-rs/src/gnark/field.rs index 7c50a9e2..d8fcd01b 100644 --- a/circuit-std-rs/src/gnark/field.rs +++ b/circuit-std-rs/src/gnark/field.rs @@ -2,6 +2,7 @@ use crate::gnark::element::*; use crate::gnark::emparam::FieldParams; use crate::gnark::utils::*; use crate::logup::LogUpRangeProofTable; +use crate::sha256::m31_utils::to_binary; use crate::utils::simple_select; use expander_compiler::frontend::*; use num_bigint::BigInt; @@ -120,6 +121,13 @@ impl GField { } res0 } + pub fn get_element_sign>( + &mut self, + native: &mut B, + x: &Element, + ) -> Variable { + to_binary(native, x.limbs[0], 30)[0] + } pub fn select>( &mut self, native: &mut B, @@ -366,7 +374,14 @@ impl GField { }; self.mul_checks.push(mc); } - pub fn assert_isequal>( + pub fn copy>(&mut self, native: &mut B, x: &Element) -> Element { + let inputs = vec![x.my_clone()]; + let output = self.new_hint(native, "myhint.copyelementhint", 1, inputs); + let res = output[0].my_clone(); + self.assert_is_equal(native, x, &res); + res + } + pub fn assert_is_equal>( &mut self, native: &mut B, a: &Element, @@ -506,16 +521,9 @@ impl GField { let div = self.compute_division_hint(native, a.limbs.clone(), b.limbs.clone()); let e = self.pack_limbs(native, div, true); let res = self.mul(native, &e, &new_b); - self.assert_isequal(native, &res, &new_a); + self.assert_is_equal(native, &res, &new_a); e } - /* - mulOf, err := f.mulPreCond(a, &Element[T]{Limbs: make([]frontend.Variable, f.fParams.NbLimbs()), overflow: 0}) // order is important, we want that reduce left side - if err != nil { - return mulOf, err - } - return f.subPreCond(&Element[T]{overflow: 0}, &Element[T]{overflow: mulOf}) - */ pub fn inverse>( &mut self, native: &mut B, @@ -540,7 +548,7 @@ impl GField { let e = self.pack_limbs(native, inv, true); let res = self.mul(native, &e, &new_b); let one = self.one_const.my_clone(); - self.assert_isequal(native, &res, &one); + self.assert_is_equal(native, &res, &one); e } pub fn compute_inverse_hint>( @@ -636,6 +644,54 @@ impl GField { self.mul_checks[i].clean_evaluations(); } } + pub fn hash_to_fp>( + &mut self, + native: &mut B, + msg: &[Variable], + len: usize, + ) -> Vec> { + let signature_dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + let mut dst = vec![]; + for c in signature_dst { + dst.push(native.constant(*c as u32)); + } + let hm = hash_to_fp_variable(native, msg, &dst, len); + let mut xs_limbs = vec![]; + let n = T::bits_per_limb(); + if n != 8 { + panic!("only support 8 bits per limb for now"); + } + let k = T::nb_limbs() as usize; + if k > 64 { + panic!("only support <= 64 limbs for now"); + } + for element in &hm { + let mut x = vec![]; + for j in 0..k { + x.push(element[k - 1 - j]); + } + xs_limbs.push(x); + } + let shift = value_of( + native, + Box::new("340282366920938463463374607431768211456".to_string()), + ); + let mut x_elements = vec![]; + for i in 0..xs_limbs.len() { + let mut x_element = new_internal_element(xs_limbs[i].clone(), 0); + x_element = self.mul(native, &x_element, &shift); + let mut x_rem = vec![native.constant(0); k]; + for (j, rem) in x_rem.iter_mut().enumerate().take(k) { + if j < (64 - k) { + *rem = hm[i][63 - j]; + } + } + x_element = self.add(native, &x_element, &new_internal_element(x_rem, 0)); + x_element = self.reduce(native, &x_element, true); + x_elements.push(x_element); + } + x_elements + } } pub fn eval_with_challenge, T: FieldParams>( native: &mut B, diff --git a/circuit-std-rs/src/gnark/hints.rs b/circuit-std-rs/src/gnark/hints.rs index 1b4b25e3..3976ae56 100644 --- a/circuit-std-rs/src/gnark/hints.rs +++ b/circuit-std-rs/src/gnark/hints.rs @@ -1,14 +1,11 @@ use crate::gnark::limbs::*; use crate::gnark::utils::*; -use crate::logup::{query_count_by_key_hint, query_count_hint, rangeproof_hint}; -use crate::sha256::m31_utils::to_binary_hint; use ark_bls12_381::Fq; use ark_bls12_381::Fq12; use ark_bls12_381::Fq2; use ark_bls12_381::Fq6; use ark_ff::fields::Field; use ark_ff::Zero; -use expander_compiler::frontend::extra::*; use expander_compiler::frontend::*; use num_bigint::BigInt; use num_bigint::BigUint; @@ -17,27 +14,6 @@ use num_traits::Signed; use num_traits::ToPrimitive; use std::str::FromStr; -pub fn register_hint(hint_registry: &mut HintRegistry) { - hint_registry.register("myhint.tobinary", to_binary_hint); - hint_registry.register("myhint.mulhint", mul_hint); - hint_registry.register("myhint.simple_rangecheck_hint", simple_rangecheck_hint); - hint_registry.register("myhint.querycounthint", query_count_hint); - hint_registry.register("myhint.querycountbykeyhint", query_count_by_key_hint); - hint_registry.register("myhint.copyvarshint", copy_vars_hint); - hint_registry.register("myhint.divhint", div_hint); - hint_registry.register("myhint.invhint", inv_hint); - hint_registry.register("myhint.dive2hint", div_e2_hint); - hint_registry.register("myhint.inversee2hint", inverse_e2_hint); - hint_registry.register("myhint.copye2hint", copy_e2_hint); - hint_registry.register("myhint.dive6hint", div_e6_hint); - hint_registry.register("myhint.inversee6hint", inverse_e6_hint); - hint_registry.register("myhint.dive6by6hint", div_e6_by_6_hint); - hint_registry.register("myhint.dive12hint", div_e12_hint); - hint_registry.register("myhint.inversee12hint", inverse_e12_hint); - hint_registry.register("myhint.copye12hint", copy_e12_hint); - hint_registry.register("myhint.finalexphint", final_exp_hint); - hint_registry.register("myhint.rangeproofhint", rangeproof_hint); -} pub fn mul_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { let nb_bits = inputs[0].to_u256().as_usize(); let nb_limbs = inputs[1].to_u256().as_usize(); @@ -812,6 +788,19 @@ pub fn copy_vars_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> outputs.copy_from_slice(&inputs[..outputs.len()]); Ok(()) } +pub fn copy_element_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { + if let Err(err) = unwrap_hint( + true, + true, + inputs, + outputs, + //copyE2Hint + |inputs| inputs, + ) { + panic!("copyElementHint: {}", err); + } + Ok(()) +} pub fn copy_e2_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { if let Err(err) = unwrap_hint( true, @@ -825,6 +814,160 @@ pub fn copy_e2_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { } Ok(()) } +pub fn get_element_sqrt_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { + if let Err(err) = unwrap_hint( + true, + true, + inputs, + outputs, + //getElementSqrtHint + |inputs| { + let biguint_inputs = inputs + .iter() + .map(|x| x.to_biguint().unwrap()) + .collect::>(); + let a = Fq::from(biguint_inputs[0].clone()); + let (sqrt, is_square) = fq_has_sqrt(&a); + let sqrt_bigint = sqrt + .to_string() + .parse::() + .expect("Invalid decimal string"); + vec![BigInt::from(is_square), sqrt_bigint] + }, + ) { + panic!("getElementSqrtHint: {}", err); + } + Ok(()) +} +pub fn get_e2_sqrt_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { + if let Err(err) = unwrap_hint( + true, + true, + inputs, + outputs, + //getElementSqrtHint + |inputs| { + let biguint_inputs = inputs + .iter() + .map(|x| x.to_biguint().unwrap()) + .collect::>(); + let a0 = Fq::from(biguint_inputs[0].clone()); + let a1 = Fq::from(biguint_inputs[1].clone()); + let a = Fq2::new(a0, a1); + let (sqrt, is_square) = fq2_has_sqrt(&a); + let sqrt0_bigint = sqrt + .c0 + .to_string() + .parse::() + .expect("Invalid decimal string"); + let sqrt1_bigint = sqrt + .c1 + .to_string() + .parse::() + .expect("Invalid decimal string"); + vec![BigInt::from(is_square), sqrt0_bigint, sqrt1_bigint] + }, + ) { + panic!("getElementSqrtHint: {}", err); + } + Ok(()) +} +pub fn get_sqrt_x0x1_fq_new_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { + if let Err(err) = unwrap_hint( + true, + true, + inputs, + outputs, + //divE12Hint + |inputs| { + let biguint_inputs = inputs + .iter() + .map(|x| x.to_biguint().unwrap()) + .collect::>(); + + let g_x0 = Fq::from(biguint_inputs[0].clone()); + let g_x1 = Fq::from(biguint_inputs[1].clone()); + let t = Fq::from(biguint_inputs[2].clone()); + let sgn_t = get_fq_sign(&t); + let (g_x0_sqrt, is_square0) = fq_has_sqrt(&g_x0); + let (g_x1_sqrt, is_square1) = fq_has_sqrt(&g_x1); + let mut y; + if is_square0 { + y = g_x0_sqrt; + } else if is_square1 { + y = g_x1_sqrt; + } else { + panic!("At least one should be square"); + } + let sgn_y = get_fq_sign(&y); + if sgn_y != sgn_t { + y = -y; + } + let y_bigint = y + .to_string() + .parse::() + .expect("Invalid decimal string"); + vec![BigInt::from(is_square0), y_bigint] + }, + ) { + panic!("divE2Hint: {}", err); + } + Ok(()) +} +pub fn get_sqrt_x0x1_fq2_new_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { + if let Err(err) = unwrap_hint( + true, + true, + inputs, + outputs, + //divE12Hint + |inputs| { + let biguint_inputs = inputs + .iter() + .map(|x| x.to_biguint().unwrap()) + .collect::>(); + + let g_x0_a0 = Fq::from(biguint_inputs[0].clone()); + let g_x0_a1 = Fq::from(biguint_inputs[1].clone()); + let g_x1_a0 = Fq::from(biguint_inputs[2].clone()); + let g_x1_a1 = Fq::from(biguint_inputs[3].clone()); + let t_a0 = Fq::from(biguint_inputs[4].clone()); + let t_a1 = Fq::from(biguint_inputs[5].clone()); + + let g_x0 = Fq2::new(Fq::from(g_x0_a0), Fq::from(g_x0_a1)); + let g_x1 = Fq2::new(Fq::from(g_x1_a0), Fq::from(g_x1_a1)); + let t = Fq2::new(Fq::from(t_a0), Fq::from(t_a1)); + let sgn_t = get_fq2_sign(&t); + let (g_x0_sqrt, is_square0) = fq2_has_sqrt(&g_x0); + let (g_x1_sqrt, is_square1) = fq2_has_sqrt(&g_x1); + let mut y; + if is_square0 { + y = g_x0_sqrt; + } else if is_square1 { + y = g_x1_sqrt; + } else { + panic!("At least one should be square"); + } + let sgn_y = get_fq2_sign(&y); + if sgn_y != sgn_t { + y.c0 = -y.c0; + y.c1 = -y.c1; + } + let y0_c0_bigint = + y.c0.to_string() + .parse::() + .expect("Invalid decimal string"); + let y0_c1_bigint = + y.c1.to_string() + .parse::() + .expect("Invalid decimal string"); + vec![BigInt::from(is_square0), y0_c0_bigint, y0_c1_bigint] + }, + ) { + panic!("divE2Hint: {}", err); + } + Ok(()) +} pub fn copy_e12_hint(inputs: &[M31], outputs: &mut [M31]) -> Result<(), Error> { if let Err(err) = unwrap_hint( true, diff --git a/circuit-std-rs/src/gnark/utils.rs b/circuit-std-rs/src/gnark/utils.rs index 4b09ff93..cef74402 100644 --- a/circuit-std-rs/src/gnark/utils.rs +++ b/circuit-std-rs/src/gnark/utils.rs @@ -1,3 +1,5 @@ +use ark_bls12_381::Fq; +use ark_ff::Field; use num_bigint::BigInt; use crate::gnark::element::*; @@ -5,6 +7,11 @@ use crate::gnark::emparam::FieldParams; use crate::gnark::emulated::field_bls12381::e2::GE2; use crate::gnark::limbs::decompose; use crate::gnark::limbs::recompose; +use crate::sha256::m31::sha256_var_bytes; +use crate::sha256::m31_utils::from_binary; +use crate::sha256::m31_utils::to_binary; +use ark_bls12_381::Fq2; +use ark_ff::Zero; use expander_compiler::frontend::*; pub fn nb_multiplication_res_limbs(len_left: usize, len_right: usize) -> usize { @@ -43,6 +50,123 @@ pub fn sub_padding( new_pad } +pub fn get_fq_sign(x: &Fq) -> bool { + let x_bigint = x + .to_string() + .parse::() + .expect("Invalid decimal string"); + !(x_bigint % 2u32).is_zero() +} +pub fn get_fq2_sign(x: &Fq2) -> bool { + let x_a0 = + x.c0.to_string() + .parse::() + .expect("Invalid decimal string"); + let x_a1 = + x.c1.to_string() + .parse::() + .expect("Invalid decimal string"); + let z = x_a0.is_zero(); + let sgn0 = !(x_a0 % 2u32).is_zero(); + let sgn1 = !(x_a1 % 2u32).is_zero(); + sgn0 | (z & sgn1) +} +pub fn fq_has_sqrt(x: &Fq) -> (Fq, bool) { + match x.sqrt() { + Some(sqrt_x) => (sqrt_x, true), + None => (*x, false), + } +} +pub fn fq2_has_sqrt(x: &Fq2) -> (Fq2, bool) { + match x.sqrt() { + Some(sqrt_x) => (sqrt_x, true), + None => (*x, false), + } +} +pub fn xor_variable>( + api: &mut B, + nbits: usize, + a: Variable, + b: Variable, +) -> Variable { + let bits_a = to_binary(api, a, nbits); + let bits_b = to_binary(api, b, nbits); + let mut bits_res = vec![Variable::default(); nbits]; + for i in 0..nbits { + bits_res[i] = api.xor(bits_a[i], bits_b[i]); + } + from_binary(api, bits_res) +} +pub fn expand_msg_xmd_variable>( + api: &mut B, + msg: &[Variable], + dst: &[Variable], + len_in_bytes: usize, +) -> Vec { + let ell = (len_in_bytes + 31) / 32; + if ell > 255 { + panic!("invalid lenInBytes"); + } + if dst.len() > 255 { + panic!("invalid domain size (>255 bytes)"); + } + let size_domain = dst.len() as u8; + let mut block_v = vec![Variable::default(); 64]; + for v in &mut block_v { + *v = api.constant(0); + } + let mut input = Vec::new(); + input.extend_from_slice(&block_v); + input.extend_from_slice(msg); + input.push(api.constant((len_in_bytes >> 8) as u32)); + input.push(api.constant(len_in_bytes as u32)); + input.push(api.constant(0)); + input.extend_from_slice(dst); + input.push(api.constant(size_domain as u32)); + let b0 = sha256_var_bytes(api, &input); + input.clear(); + input.extend_from_slice(&b0); + input.push(api.constant(1)); + input.extend_from_slice(dst); + input.push(api.constant(size_domain as u32)); + let mut b1 = sha256_var_bytes(api, &input); + let mut res = b1.clone(); + for i in 2..=ell { + let mut strxor = vec![Variable::default(); 32]; + for j in 0..32 { + strxor[j] = xor_variable(api, 8, b0[j], b1[j]); + } + input.clear(); + input.extend_from_slice(&strxor); + input.push(api.constant(i as u32)); + input.extend_from_slice(dst); + input.push(api.constant(size_domain as u32)); + b1 = sha256_var_bytes(api, &input); + res.extend_from_slice(&b1); + } + res +} + +pub fn hash_to_fp_variable>( + api: &mut B, + msg: &[Variable], + dst: &[Variable], + count: usize, +) -> Vec> { + const FP_BITS: usize = 381; + let bytes = 1 + (FP_BITS - 1) / 8; + let l = 16 + bytes; + let len_in_bytes = count * l; + let pseudo_random_bytes = expand_msg_xmd_variable(api, msg, dst, len_in_bytes); + let mut elems = vec![vec![Variable::default(); l]; count]; + for i in 0..count { + for j in 0..l { + elems[i][j] = pseudo_random_bytes[i * l + j]; + } + } + elems +} + pub fn print_e2>(native: &mut B, v: &GE2) { for i in 0..48 { println!( diff --git a/circuit-std-rs/src/lib.rs b/circuit-std-rs/src/lib.rs index 3baeade3..b5b600fb 100644 --- a/circuit-std-rs/src/lib.rs +++ b/circuit-std-rs/src/lib.rs @@ -4,6 +4,8 @@ pub use traits::StdCircuit; pub mod logup; pub use logup::{LogUpCircuit, LogUpParams}; +pub mod matmul; + pub mod gnark; pub mod poseidon_m31; pub mod sha256; diff --git a/circuit-std-rs/src/logup.rs b/circuit-std-rs/src/logup.rs index 4b399d50..b92e5bd9 100644 --- a/circuit-std-rs/src/logup.rs +++ b/circuit-std-rs/src/logup.rs @@ -150,7 +150,7 @@ fn logup_poly_val>( } impl Define for LogUpCircuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let key_len = self.table_keys[0].len(); let value_len = self.table_values[0].len(); diff --git a/circuit-std-rs/src/matmul.rs b/circuit-std-rs/src/matmul.rs new file mode 100644 index 00000000..6700c481 --- /dev/null +++ b/circuit-std-rs/src/matmul.rs @@ -0,0 +1,184 @@ +use crate::StdCircuit; +use arith::Field; +use expander_compiler::frontend::*; +use std::convert::From; +use std::ops::{AddAssign, Mul}; + +#[derive(Clone, Copy, Debug)] +pub struct MatMulParams { + pub m1: usize, + pub n1: usize, + pub m2: usize, + pub n2: usize, +} + +declare_circuit!(_MatMulCircuit { + // first matrix + first_mat: [[Variable]], + // second matrix + second_mat: [[Variable]], + // result matrix + result_mat: [[Variable]], +}); + +pub type MatMulCircuit = _MatMulCircuit; + +impl Define for MatMulCircuit { + fn define>(&self, builder: &mut Builder) { + // [m1,n1] represents the first matrix's dimension + let m1 = self.first_mat.len(); + let n1 = self.first_mat[0].len(); + + // [m2,n2] represents the second matrix's dimension + let m2 = self.second_mat.len(); + let n2 = self.second_mat[0].len(); + + // [r1,r2] represents the result matrix's dimension + let r1 = self.result_mat.len(); + let r2 = self.result_mat[0].len(); + let zero = builder.constant(0); + + builder.assert_is_equal(Variable::from(n1), Variable::from(m2)); + builder.assert_is_equal(Variable::from(r1), Variable::from(m1)); + builder.assert_is_equal(Variable::from(r2), Variable::from(n2)); + + let loop_count = if C::CircuitField::SIZE == M31::SIZE { + 3 + } else { + 1 + }; + + for _ in 0..loop_count { + let randomness = builder.get_random_value(); + let mut aux_mat = Vec::new(); + let mut challenge = randomness; + + // construct the aux matrix = [1, randomness, randomness^2, ..., randomness^(n-1)] + aux_mat.push(Variable::from(1)); + for _ in 0..n2 - 1 { + challenge = builder.mul(challenge, randomness); + aux_mat.push(challenge); + } + + let mut aux_second = vec![zero; m2]; + let mut aux_first = vec![zero; m1]; + let mut aux_res = vec![zero; m1]; + + // calculate second_mat * aux_mat, + self.matrix_multiply(builder, &mut aux_second, &aux_mat, &self.second_mat); + // calculate result_mat * aux_second + self.matrix_multiply(builder, &mut aux_res, &aux_mat, &self.result_mat); + // calculate first_mat * aux_second + self.matrix_multiply(builder, &mut aux_first, &aux_second, &self.first_mat); + + // compare aux_first with aux_res + for i in 0..m1 { + builder.assert_is_equal(aux_first[i], aux_res[i]); + } + } + } +} + +impl MatMulCircuit { + // calculate origin_mat * aux_mat and store the result into target_mat + fn matrix_multiply( + &self, + builder: &mut impl RootAPI, + target_mat: &mut [Variable], // target to modify + aux_mat: &[Variable], + origin_mat: &[Vec], + ) { + // for i in 0..target_mat.len{ + // for j in 0..aux_mat.len { + // let mul_result = builder.mul(origin_mat[i][j], aux_mat[j]); + // target_mat[i] = builder.add(target_mat[i], mul_result); + // } + // } + for (i, target_item) in target_mat.iter_mut().enumerate() { + for (j, item) in aux_mat.iter().enumerate() { + let mul_result = builder.mul(origin_mat[i][j], item); + *target_item = builder.add(*target_item, mul_result); + } + } + } +} + +impl StdCircuit for MatMulCircuit { + type Params = MatMulParams; + type Assignment = _MatMulCircuit; + + fn new_circuit(params: &Self::Params) -> Self { + let mut circuit = Self::default(); + + circuit + .first_mat + .resize(params.m1, vec![Variable::default(); params.n1]); + circuit + .second_mat + .resize(params.m2, vec![Variable::default(); params.n2]); + + circuit + .result_mat + .resize(params.m1, vec![Variable::default(); params.n2]); + + circuit + } + + fn new_assignment(params: &Self::Params, mut rng: impl rand::RngCore) -> Self::Assignment { + let mut assignment = _MatMulCircuit::::default(); + assignment + .first_mat + .resize(params.m1, vec![C::CircuitField::zero(); params.n1]); + assignment + .second_mat + .resize(params.m2, vec![C::CircuitField::zero(); params.n2]); + assignment + .result_mat + .resize(params.m1, vec![C::CircuitField::zero(); params.n2]); + + for i in 0..params.m1 { + for j in 0..params.n1 { + assignment.first_mat[i][j] = C::CircuitField::random_unsafe(&mut rng); + } + } + for i in 0..params.m2 { + for j in 0..params.n2 { + assignment.second_mat[i][j] = C::CircuitField::random_unsafe(&mut rng); + } + } + + // initialize the aux matrix with random values. + // result matrix should be computed + assignment.result_mat = matrix_multiply::(&assignment.first_mat, &assignment.second_mat); + + assignment + } +} + +// this helper calculates matrix c = a * b; +#[allow(clippy::needless_range_loop)] +fn matrix_multiply( + a: &[Vec], + b: &[Vec], +) -> Vec> { + let m1 = a.len(); + let n1 = a[0].len(); + let m2 = b.len(); + let n2 = b[0].len(); + + assert_eq!(n1, m2, "n1 ! = m2 "); + + // initialize the result matrix + let mut c = vec![vec![C::CircuitField::default(); n2]; m1]; + + // FIXME: optimize calculating the multiplication for super large matrix. + for i in 0..m1 { + for j in 0..n2 { + for k in 0..n1 { + c[i][j].add_assign(a[i][k].mul(b[k][j])); + } + } + } + + c +} diff --git a/circuit-std-rs/src/poseidon_m31.rs b/circuit-std-rs/src/poseidon_m31.rs index edee2e6d..f0801e66 100644 --- a/circuit-std-rs/src/poseidon_m31.rs +++ b/circuit-std-rs/src/poseidon_m31.rs @@ -177,6 +177,27 @@ impl PoseidonM31Params { self.permute(api, &mut res) }); + res + } + pub fn hash_to_state_flatten>( + &self, + api: &mut B, + inputs: &[Variable], + ) -> Vec { + let mut elts = inputs.to_vec(); + elts.resize(elts.len().next_multiple_of(self.rate), api.constant(0)); + + let mut res = vec![api.constant(0); self.width]; + let mut copy_res = api.new_hint("myhint.copyvarshint", &res, res.len()); + elts.chunks(self.rate).for_each(|chunk| { + let mut state_elts = vec![api.constant(0); self.width - self.rate]; + state_elts.extend_from_slice(chunk); + + (0..self.width).for_each(|i| res[i] = api.add(copy_res[i], state_elts[i])); + self.permute(api, &mut res); + copy_res = api.new_hint("myhint.copyvarshint", &res, res.len()); + }); + res } } diff --git a/circuit-std-rs/src/sha256/m31.rs b/circuit-std-rs/src/sha256/m31.rs index d39d10b2..fcab60c1 100644 --- a/circuit-std-rs/src/sha256/m31.rs +++ b/circuit-std-rs/src/sha256/m31.rs @@ -287,3 +287,47 @@ pub fn sha256_37bytes>( d.chunk_write(builder, &data); d.return_sum(builder).to_vec() } + +pub fn sha256_var_bytes>( + builder: &mut B, + orign_data: &[Variable], +) -> Vec { + let mut data = orign_data.to_vec(); + let n = data.len(); + let n_bytes = (n * 8).to_be_bytes().to_vec(); + let mut pad; + if n % 64 > 55 { + //need to add one more chunk (64bytes) + pad = vec![builder.constant(0); 128 - n % 64]; + pad[0] = builder.constant(128); //0x80 + } else { + pad = vec![builder.constant(0); 64 - n % 64]; + pad[0] = builder.constant(128); //0x80 + } + let pad_len = pad.len(); + for i in 0..n_bytes.len() { + pad[pad_len - n_bytes.len() + i] = builder.constant(n_bytes[i] as u32); + } + data.append(&mut pad); //append padding + + let mut d = MyDigest::new(builder); + d.reset(builder); + + let n = data.len(); + for i in 0..n / 64 { + d.chunk_write(builder, &data[i * 64..(i + 1) * 64]); + } + d.return_sum(builder).to_vec() +} + +pub fn check_sha256_37bytes>( + builder: &mut B, + origin_data: &[Variable], +) -> Vec { + let output = origin_data[37..].to_vec(); + let result = sha256_37bytes(builder, &origin_data[..37]); + for i in 0..32 { + builder.assert_is_equal(result[i], output[i]); + } + result +} diff --git a/circuit-std-rs/src/sha256/m31_utils.rs b/circuit-std-rs/src/sha256/m31_utils.rs index 32942a80..e4628131 100644 --- a/circuit-std-rs/src/sha256/m31_utils.rs +++ b/circuit-std-rs/src/sha256/m31_utils.rs @@ -331,7 +331,7 @@ declare_circuit!(IDIVMODBITCircuit { }); impl Define for IDIVMODBITCircuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let (quotient, remainder) = idiv_mod_bit(builder, self.value, 8); builder.assert_is_equal(quotient, self.quotient); builder.assert_is_equal(remainder, self.remainder); @@ -343,7 +343,7 @@ fn test_idiv_mod_bit() { let mut hint_registry = HintRegistry::::new(); hint_registry.register("myhint.tobinary", to_binary_hint); //compile and test - let compile_result = compile(&IDIVMODBITCircuit::default()).unwrap(); + let compile_result = compile(&IDIVMODBITCircuit::default(), CompileOptions::default()).unwrap(); let assignment = IDIVMODBITCircuit:: { value: M31::from(3845), quotient: M31::from(15), @@ -365,7 +365,7 @@ declare_circuit!(BITCONVERTCircuit { }); impl Define for BITCONVERTCircuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let mut big_int_bytes = [builder.constant(0); 8]; big_endian_put_uint64(builder, &mut big_int_bytes, self.big_int); for (i, big_int_byte) in big_int_bytes.iter().enumerate() { @@ -384,7 +384,7 @@ fn test_bit_convert() { let mut hint_registry = HintRegistry::::new(); hint_registry.register("myhint.tobinary", to_binary_hint); //compile and test - let compile_result = compile(&BITCONVERTCircuit::default()).unwrap(); + let compile_result = compile(&BITCONVERTCircuit::default(), CompileOptions::default()).unwrap(); let assignment = BITCONVERTCircuit:: { big_int: M31::from(3845), big_int_bytes: [ diff --git a/circuit-std-rs/src/utils.rs b/circuit-std-rs/src/utils.rs index 898fe24e..4795bdb0 100644 --- a/circuit-std-rs/src/utils.rs +++ b/circuit-std-rs/src/utils.rs @@ -1,5 +1,7 @@ use expander_compiler::frontend::*; +use crate::{gnark::hints::*, logup::*, sha256::m31_utils::to_binary_hint}; + pub fn simple_select>( native: &mut B, selector: Variable, @@ -28,3 +30,30 @@ pub fn simple_lookup2>( let tmp1 = simple_select(native, selector0, i3, i2); simple_select(native, selector1, tmp1, tmp0) } + +pub fn register_hint(hint_registry: &mut HintRegistry) { + hint_registry.register("myhint.tobinary", to_binary_hint); + hint_registry.register("myhint.mulhint", mul_hint); + hint_registry.register("myhint.simple_rangecheck_hint", simple_rangecheck_hint); + hint_registry.register("myhint.querycounthint", query_count_hint); + hint_registry.register("myhint.querycountbykeyhint", query_count_by_key_hint); + hint_registry.register("myhint.copyvarshint", copy_vars_hint); + hint_registry.register("myhint.divhint", div_hint); + hint_registry.register("myhint.invhint", inv_hint); + hint_registry.register("myhint.copyelementhint", copy_element_hint); + hint_registry.register("myhint.dive2hint", div_e2_hint); + hint_registry.register("myhint.inversee2hint", inverse_e2_hint); + hint_registry.register("myhint.copye2hint", copy_e2_hint); + hint_registry.register("myhint.dive6hint", div_e6_hint); + hint_registry.register("myhint.inversee6hint", inverse_e6_hint); + hint_registry.register("myhint.dive6by6hint", div_e6_by_6_hint); + hint_registry.register("myhint.dive12hint", div_e12_hint); + hint_registry.register("myhint.inversee12hint", inverse_e12_hint); + hint_registry.register("myhint.copye12hint", copy_e12_hint); + hint_registry.register("myhint.finalexphint", final_exp_hint); + hint_registry.register("myhint.rangeproofhint", rangeproof_hint); + hint_registry.register("myhint.getsqrtx0x1fq2newhint", get_sqrt_x0x1_fq2_new_hint); + hint_registry.register("myhint.getsqrtx0x1fqnewhint", get_sqrt_x0x1_fq_new_hint); + hint_registry.register("myhint.getelementsqrthint", get_element_sqrt_hint); + hint_registry.register("myhint.gete2sqrthint", get_e2_sqrt_hint); +} diff --git a/circuit-std-rs/tests/common.rs b/circuit-std-rs/tests/common.rs index bf777187..396d9fbb 100644 --- a/circuit-std-rs/tests/common.rs +++ b/circuit-std-rs/tests/common.rs @@ -9,7 +9,8 @@ where Cir: StdCircuit, { let mut rng = thread_rng(); - let compile_result: CompileResult = compile(&Cir::new_circuit(params)).unwrap(); + let compile_result: CompileResult = + compile(&Cir::new_circuit(params), CompileOptions::default()).unwrap(); let assignment = Cir::new_assignment(params, &mut rng); let witness = compile_result .witness_solver diff --git a/circuit-std-rs/tests/gnark/element.rs b/circuit-std-rs/tests/gnark/element.rs index f5fce973..461f0e69 100644 --- a/circuit-std-rs/tests/gnark/element.rs +++ b/circuit-std-rs/tests/gnark/element.rs @@ -1,10 +1,15 @@ #[cfg(test)] mod tests { - use circuit_std_rs::gnark::{ - element::{from_interface, value_of}, - emparam::Bls12381Fp, + use circuit_std_rs::{ + gnark::{ + element::{from_interface, new_internal_element, value_of}, + emparam::Bls12381Fp, + field::GField, + }, + utils::register_hint, }; use expander_compiler::frontend::*; + use extra::debug_eval; use num_bigint::BigInt; #[test] fn test_from_interface() { @@ -38,8 +43,8 @@ mod tests { target: [[Variable; 48]; 8], }); impl Define for VALUECircuit { - fn define(&self, builder: &mut API) { - let v1 = 1111111u32; + fn define>(&self, builder: &mut Builder) { + let v1 = -1111111i32; let v2 = 22222222222222u64; let v3 = 333333usize; let v4 = 444444i32; @@ -56,8 +61,13 @@ mod tests { let r6 = value_of::(builder, Box::new(v6)); let r7 = value_of::(builder, Box::new(v7)); let r8 = value_of::(builder, Box::new(v8)); - let rs = vec![r1, r2, r3, r4, r5, r6, r7, r8]; - for i in 0..rs.len() { + let rs = vec![r1.clone(), r2, r3, r4, r5, r6, r7, r8]; + let mut fp = GField::new(builder, Bls12381Fp {}); + let expect_r1 = new_internal_element::(self.target[0].to_vec(), 0); + let r1_zero = fp.add(builder, &r1.clone(), &expect_r1); + let zero = fp.zero_const.clone(); + fp.assert_is_equal(builder, &r1_zero, &zero); + for i in 1..rs.len() { for j in 0..rs[i].limbs.len() { builder.assert_is_equal(rs[i].limbs[j], self.target[i][j]); } @@ -78,18 +88,14 @@ mod tests { 0x08080808, ]; let values_u8: Vec> = values.iter().map(|v| v.to_le_bytes().to_vec()).collect(); - let compile_result = compile(&VALUECircuit::default()).unwrap(); let mut assignment = VALUECircuit::::default(); for i in 0..values_u8.len() { for j in 0..values_u8[i].len() { assignment.target[i][j] = M31::from(values_u8[i][j] as u32); } } - let witness = compile_result - .witness_solver - .solve_witness(&assignment) - .unwrap(); - let output = compile_result.layered_circuit.run(&witness); - assert_eq!(output, vec![true]); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + debug_eval(&VALUECircuit::default(), &assignment, hint_registry); } } diff --git a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e12.rs b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e12.rs index fb9ca916..3efd1d06 100644 --- a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e12.rs +++ b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e12.rs @@ -1,18 +1,19 @@ -use circuit_std_rs::gnark::{ - element::new_internal_element, - emulated::field_bls12381::{ - e12::{Ext12, GE12}, - e2::GE2, - e6::GE6, +use circuit_std_rs::{ + gnark::{ + element::new_internal_element, + emulated::field_bls12381::{ + e12::{Ext12, GE12}, + e2::GE2, + e6::GE6, + }, }, - hints::register_hint, + utils::register_hint, }; use expander_compiler::{ compile::CompileOptions, declare_circuit, frontend::{ - compile_generic, extra::debug_eval, GenericDefine, HintRegistry, M31Config, RootAPI, - Variable, M31, + compile, extra::debug_eval, Define, HintRegistry, M31Config, RootAPI, Variable, M31, }, }; @@ -22,7 +23,7 @@ declare_circuit!(E12AddCircuit { z: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12AddCircuit { +impl Define for E12AddCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); let x_e12 = GE12 { @@ -125,7 +126,7 @@ impl GenericDefine for E12AddCircuit { } #[test] fn test_e12_add() { - compile_generic(&E12AddCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12AddCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E12AddCircuit:: { @@ -362,7 +363,7 @@ declare_circuit!(E12SubCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12SubCircuit { +impl Define for E12SubCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -470,7 +471,7 @@ impl GenericDefine for E12SubCircuit { #[test] fn test_e12_sub() { - compile_generic(&E12SubCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12SubCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -709,7 +710,7 @@ declare_circuit!(E12MulCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12MulCircuit { +impl Define for E12MulCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -817,7 +818,7 @@ impl GenericDefine for E12MulCircuit { #[test] fn test_e12_mul() { - compile_generic(&E12MulCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12MulCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1055,7 +1056,7 @@ declare_circuit!(E12DivCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12DivCircuit { +impl Define for E12DivCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -1163,7 +1164,7 @@ impl GenericDefine for E12DivCircuit { #[test] fn test_e12_div() { - compile_generic(&E12DivCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12DivCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1400,7 +1401,7 @@ declare_circuit!(E12SquareCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12SquareCircuit { +impl Define for E12SquareCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -1477,7 +1478,7 @@ impl GenericDefine for E12SquareCircuit { #[test] fn test_e12_square() { - compile_generic(&E12SquareCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12SquareCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1641,7 +1642,7 @@ declare_circuit!(E12ConjugateCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12ConjugateCircuit { +impl Define for E12ConjugateCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -1718,7 +1719,7 @@ impl GenericDefine for E12ConjugateCircuit { #[test] fn test_e12_conjugate() { - compile_generic(&E12ConjugateCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12ConjugateCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1882,7 +1883,7 @@ declare_circuit!(E12InverseCircuit { c: [[[[Variable; 48]; 2]; 3]; 2], }); -impl GenericDefine for E12InverseCircuit { +impl Define for E12InverseCircuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); @@ -1959,7 +1960,7 @@ impl GenericDefine for E12InverseCircuit { #[test] fn test_e12_inverse() { - compile_generic(&E12InverseCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E12InverseCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -2124,7 +2125,7 @@ declare_circuit!(E12MulBy014Circuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E12MulBy014Circuit { +impl Define for E12MulBy014Circuit { fn define>(&self, builder: &mut Builder) { let mut ext12 = Ext12::new(builder); diff --git a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e2.rs b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e2.rs index a21653bf..5bb87028 100644 --- a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e2.rs +++ b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e2.rs @@ -1,13 +1,15 @@ -use circuit_std_rs::gnark::{ - element::new_internal_element, - emulated::field_bls12381::e2::{Ext2, GE2}, - hints::register_hint, +use circuit_std_rs::{ + gnark::{ + element::new_internal_element, + emulated::field_bls12381::e2::{Ext2, GE2}, + }, + utils::register_hint, }; -use expander_compiler::frontend::compile_generic; +use expander_compiler::frontend::compile; use expander_compiler::{ compile::CompileOptions, declare_circuit, - frontend::{extra::debug_eval, GenericDefine, HintRegistry, M31Config, RootAPI, Variable, M31}, + frontend::{extra::debug_eval, Define, HintRegistry, M31Config, RootAPI, Variable, M31}, }; declare_circuit!(E2AddCircuit { x: [[Variable; 48]; 2], @@ -15,7 +17,7 @@ declare_circuit!(E2AddCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2AddCircuit { +impl Define for E2AddCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -41,7 +43,7 @@ impl GenericDefine for E2AddCircuit { #[test] fn test_e2_add() { - compile_generic(&E2AddCircuit::default(), CompileOptions::default()).unwrap(); + // compile(&E2AddCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2AddCircuit:: { @@ -71,14 +73,14 @@ fn test_e2_add() { 202, 31, 217, 66, 238, 3, 35, 127, 14, ]; let z0_bytes = [ - 218, 253, 64, 116, 175, 52, 24, 151, 151, 215, 179, 170, 76, 250, 69, 90, 88, 37, 34, 244, - 208, 51, 26, 6, 74, 174, 1, 199, 44, 146, 237, 75, 240, 250, 248, 226, 161, 68, 67, 49, - 204, 164, 203, 228, 12, 79, 238, 5, + 19, 252, 77, 22, 167, 224, 86, 207, 170, 126, 100, 101, 179, 5, 123, 204, 244, 241, 1, 219, + 167, 75, 49, 47, 215, 220, 138, 172, 4, 140, 84, 156, 139, 98, 129, 126, 131, 227, 83, 128, + 231, 209, 102, 103, 142, 234, 215, 9, ]; let z1_bytes = [ - 162, 191, 112, 190, 81, 47, 128, 118, 149, 112, 222, 152, 142, 11, 49, 60, 180, 34, 229, - 197, 248, 214, 150, 237, 125, 100, 177, 224, 222, 18, 165, 199, 250, 85, 240, 222, 198, 4, - 78, 217, 202, 6, 85, 164, 7, 27, 109, 21, + 52, 239, 235, 194, 147, 251, 219, 52, 190, 151, 43, 230, 243, 162, 249, 150, 35, 33, 35, + 209, 61, 156, 61, 109, 217, 198, 182, 43, 127, 125, 25, 134, 243, 14, 209, 120, 248, 217, + 158, 177, 221, 195, 12, 158, 46, 213, 27, 7, ]; for i in 0..48 { assignment.x[0][i] = M31::from(x0_bytes[i] as u32); @@ -89,11 +91,7 @@ fn test_e2_add() { assignment.z[1][i] = M31::from(z1_bytes[i] as u32); } - // debug_eval( - // &E2AddCircuit::default(), - // &assignment, - // hint_registry, - // ); + debug_eval(&E2AddCircuit::default(), &assignment, hint_registry); } declare_circuit!(E2SubCircuit { @@ -102,7 +100,7 @@ declare_circuit!(E2SubCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2SubCircuit { +impl Define for E2SubCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -135,7 +133,7 @@ impl GenericDefine for E2SubCircuit { #[test] fn test_e2_sub() { // let compile_result = compile(&E2SubCircuit::default()).unwrap(); - compile_generic(&E2SubCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2SubCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2SubCircuit:: { @@ -191,7 +189,7 @@ declare_circuit!(E2DoubleCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2DoubleCircuit { +impl Define for E2DoubleCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -216,7 +214,7 @@ impl GenericDefine for E2DoubleCircuit { #[test] fn test_e2_double() { // let compile_result = compile(&E2DoubleCircuit::default()).unwrap(); - compile_generic(&E2DoubleCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2DoubleCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2DoubleCircuit:: { @@ -260,7 +258,7 @@ declare_circuit!(E2MulCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2MulCircuit { +impl Define for E2MulCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -289,7 +287,7 @@ impl GenericDefine for E2MulCircuit { #[test] fn test_e2_mul() { // let compile_result = compile(&E2MulCircuit::default()).unwrap(); - compile_generic(&E2MulCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2MulCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2MulCircuit:: { @@ -346,7 +344,7 @@ declare_circuit!(E2SquareCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2SquareCircuit { +impl Define for E2SquareCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -371,7 +369,7 @@ impl GenericDefine for E2SquareCircuit { #[test] fn test_e2_square() { // let compile_result = compile(&E2SquareCircuit::default()).unwrap(); - compile_generic(&E2SquareCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2SquareCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2SquareCircuit:: { @@ -415,7 +413,7 @@ declare_circuit!(E2DivCircuit { z: [[Variable; 48]; 2], }); -impl GenericDefine for E2DivCircuit { +impl Define for E2DivCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let x_e2 = GE2 { @@ -443,7 +441,7 @@ impl GenericDefine for E2DivCircuit { #[test] fn test_e2_div() { - compile_generic(&E2DivCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2DivCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2DivCircuit:: { @@ -500,7 +498,7 @@ declare_circuit!(E2MulByElementCircuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E2MulByElementCircuit { +impl Define for E2MulByElementCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let a_e2 = GE2 { @@ -526,7 +524,7 @@ impl GenericDefine for E2MulByElementCircuit { #[test] fn test_e2_mul_by_element() { // let compile_result = compile(&E2MulByElementCircuit::default()).unwrap(); - compile_generic(&E2MulByElementCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2MulByElementCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2MulByElementCircuit:: { @@ -581,7 +579,7 @@ declare_circuit!(E2MulByNonResidueCircuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E2MulByNonResidueCircuit { +impl Define for E2MulByNonResidueCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let a_e2 = GE2 { @@ -605,7 +603,7 @@ impl GenericDefine for E2MulByNonResidueCircuit { #[test] fn test_e2_mul_by_non_residue() { - compile_generic( + compile( &E2MulByNonResidueCircuit::default(), CompileOptions::default(), ) @@ -657,7 +655,7 @@ declare_circuit!(E2NegCircuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E2NegCircuit { +impl Define for E2NegCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let a_e2 = GE2 { @@ -682,7 +680,7 @@ impl GenericDefine for E2NegCircuit { #[test] fn test_e2_neg() { // let compile_result = compile(&E2NegCircuit::default()).unwrap(); - compile_generic(&E2NegCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2NegCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2NegCircuit:: { @@ -726,7 +724,7 @@ declare_circuit!(E2ConjugateCircuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E2ConjugateCircuit { +impl Define for E2ConjugateCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let a_e2 = GE2 { @@ -751,7 +749,7 @@ impl GenericDefine for E2ConjugateCircuit { #[test] fn test_e2_conjugate() { // let compile_result = compile(&E2ConjugateCircuit::default()).unwrap(); - compile_generic(&E2ConjugateCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2ConjugateCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2ConjugateCircuit:: { @@ -795,7 +793,7 @@ declare_circuit!(E2InverseCircuit { c: [[Variable; 48]; 2], }); -impl GenericDefine for E2InverseCircuit { +impl Define for E2InverseCircuit { fn define>(&self, builder: &mut Builder) { let mut ext2 = Ext2::new(builder); let a_e2 = GE2 { @@ -819,7 +817,7 @@ impl GenericDefine for E2InverseCircuit { #[test] fn test_e2_inverse() { - compile_generic(&E2InverseCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E2InverseCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E2InverseCircuit:: { diff --git a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e6.rs b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e6.rs index bc8db2d9..51338ec7 100644 --- a/circuit-std-rs/tests/gnark/emulated/field_bls12381/e6.rs +++ b/circuit-std-rs/tests/gnark/emulated/field_bls12381/e6.rs @@ -1,17 +1,18 @@ -use circuit_std_rs::gnark::{ - element::new_internal_element, - emulated::field_bls12381::{ - e2::GE2, - e6::{Ext6, GE6}, +use circuit_std_rs::{ + gnark::{ + element::new_internal_element, + emulated::field_bls12381::{ + e2::GE2, + e6::{Ext6, GE6}, + }, }, - hints::register_hint, + utils::register_hint, }; use expander_compiler::{ compile::CompileOptions, declare_circuit, frontend::{ - compile_generic, extra::debug_eval, GenericDefine, HintRegistry, M31Config, RootAPI, - Variable, M31, + compile, extra::debug_eval, Define, HintRegistry, M31Config, RootAPI, Variable, M31, }, }; @@ -21,7 +22,7 @@ declare_circuit!(E6AddCircuit { z: [[[Variable; 48]; 2]; 3], }); -impl GenericDefine for E6AddCircuit { +impl Define for E6AddCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); let x_e6 = GE6 { @@ -77,7 +78,7 @@ impl GenericDefine for E6AddCircuit { #[test] fn test_e6_add() { // let compile_result = compile(&E2AddCircuit::default()).unwrap(); - compile_generic(&E6AddCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6AddCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = E6AddCircuit:: { @@ -207,7 +208,7 @@ declare_circuit!(E6SubCircuit { z: [[[Variable; 48]; 2]; 3], }); -impl GenericDefine for E6SubCircuit { +impl Define for E6SubCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -269,7 +270,7 @@ impl GenericDefine for E6SubCircuit { #[test] fn test_e6_sub() { - compile_generic(&E6SubCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6SubCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -399,7 +400,7 @@ declare_circuit!(E6MulCircuit { z: [[[Variable; 48]; 2]; 3], }); -impl GenericDefine for E6MulCircuit { +impl Define for E6MulCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -461,7 +462,7 @@ impl GenericDefine for E6MulCircuit { #[test] fn test_e6_mul() { - compile_generic(&E6MulCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6MulCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -591,7 +592,7 @@ declare_circuit!(E6SquareCircuit { z: [[[Variable; 48]; 2]; 3], }); -impl GenericDefine for E6SquareCircuit { +impl Define for E6SquareCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -638,7 +639,7 @@ impl GenericDefine for E6SquareCircuit { #[test] fn test_e6_square() { - compile_generic(&E6SquareCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6SquareCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -732,7 +733,7 @@ declare_circuit!(E6DivCircuit { z: [[[Variable; 48]; 2]; 3], }); -impl GenericDefine for E6DivCircuit { +impl Define for E6DivCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -925,7 +926,7 @@ declare_circuit!(E6MulByNonResidueCircuit { c: [[[Variable; 48]; 2]; 3], // Public variable }); -impl GenericDefine for E6MulByNonResidueCircuit { +impl Define for E6MulByNonResidueCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -972,7 +973,7 @@ impl GenericDefine for E6MulByNonResidueCircuit { #[test] fn test_e6_mul_by_non_residue() { - compile_generic( + compile( &E6MulByNonResidueCircuit::default(), CompileOptions::default(), ) @@ -1073,7 +1074,7 @@ declare_circuit!(E6MulByE2Circuit { c: [[[Variable; 48]; 2]; 3], // Public variable }); -impl GenericDefine for E6MulByE2Circuit { +impl Define for E6MulByE2Circuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -1125,7 +1126,7 @@ impl GenericDefine for E6MulByE2Circuit { #[test] fn test_e6_mul_by_e2() { - compile_generic(&E6MulByE2Circuit::default(), CompileOptions::default()).unwrap(); + compile(&E6MulByE2Circuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1233,7 +1234,7 @@ declare_circuit!(E6MulBy01Circuit { c: [[[Variable; 48]; 2]; 3], // Public variable }); -impl GenericDefine for E6MulBy01Circuit { +impl Define for E6MulBy01Circuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -1409,7 +1410,7 @@ declare_circuit!(E6NegCircuit { c: [[[Variable; 48]; 2]; 3], // Public variable }); -impl GenericDefine for E6NegCircuit { +impl Define for E6NegCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -1455,7 +1456,7 @@ impl GenericDefine for E6NegCircuit { #[test] fn test_e6_neg() { - compile_generic(&E6NegCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6NegCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); @@ -1547,7 +1548,7 @@ declare_circuit!(E6InverseCircuit { c: [[[Variable; 48]; 2]; 3], // Public variable }); -impl GenericDefine for E6InverseCircuit { +impl Define for E6InverseCircuit { fn define>(&self, builder: &mut Builder) { let mut ext6 = Ext6::new(builder); @@ -1593,7 +1594,7 @@ impl GenericDefine for E6InverseCircuit { #[test] fn test_e6_inverse() { - compile_generic(&E6InverseCircuit::default(), CompileOptions::default()).unwrap(); + compile(&E6InverseCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); diff --git a/circuit-std-rs/tests/gnark/emulated/sw_bls12381/g1.rs b/circuit-std-rs/tests/gnark/emulated/sw_bls12381/g1.rs index f6fcad69..1f779e8f 100644 --- a/circuit-std-rs/tests/gnark/emulated/sw_bls12381/g1.rs +++ b/circuit-std-rs/tests/gnark/emulated/sw_bls12381/g1.rs @@ -1,14 +1,12 @@ -use circuit_std_rs::gnark::{ - element::Element, - emulated::sw_bls12381::g1::{G1Affine, G1}, - hints::register_hint, +use circuit_std_rs::{ + gnark::emulated::sw_bls12381::g1::{G1Affine, G1}, + utils::register_hint, }; use expander_compiler::{ compile::CompileOptions, declare_circuit, frontend::{ - compile_generic, extra::debug_eval, GenericDefine, HintRegistry, M31Config, RootAPI, - Variable, M31, + compile, extra::debug_eval, Define, HintRegistry, M31Config, RootAPI, Variable, M31, }, }; @@ -18,69 +16,18 @@ declare_circuit!(G1AddCircuit { r: [[Variable; 48]; 2], }); -impl GenericDefine for G1AddCircuit { +impl Define for G1AddCircuit { fn define>(&self, builder: &mut Builder) { let mut g1 = G1::new(builder); - let p1_g1 = G1Affine { - x: Element::new( - self.p[0].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - y: Element::new( - self.p[1].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - }; - let p2_g1 = G1Affine { - x: Element::new( - self.q[0].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - y: Element::new( - self.q[1].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - }; - let r_g1 = G1Affine { - x: Element::new( - self.r[0].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - y: Element::new( - self.r[1].to_vec(), - 0, - false, - false, - false, - Variable::default(), - ), - }; + let p1_g1 = G1Affine::from_vars(self.p[0].to_vec(), self.p[1].to_vec()); + let p2_g1 = G1Affine::from_vars(self.q[0].to_vec(), self.q[1].to_vec()); + let r_g1 = G1Affine::from_vars(self.r[0].to_vec(), self.r[1].to_vec()); let mut r = g1.add(builder, &p1_g1, &p2_g1); for _ in 0..16 { r = g1.add(builder, &r, &p2_g1); } - g1.curve_f.assert_isequal(builder, &r.x, &r_g1.x); - g1.curve_f.assert_isequal(builder, &r.y, &r_g1.y); + g1.curve_f.assert_is_equal(builder, &r.x, &r_g1.x); + g1.curve_f.assert_is_equal(builder, &r.y, &r_g1.y); g1.curve_f.check_mul(builder); g1.curve_f.table.final_check(builder); g1.curve_f.table.final_check(builder); @@ -90,7 +37,7 @@ impl GenericDefine for G1AddCircuit { #[test] fn test_g1_add() { - compile_generic(&G1AddCircuit::default(), CompileOptions::default()).unwrap(); + compile(&G1AddCircuit::default(), CompileOptions::default()).unwrap(); let mut hint_registry = HintRegistry::::new(); register_hint(&mut hint_registry); let mut assignment = G1AddCircuit:: { diff --git a/circuit-std-rs/tests/gnark/emulated/sw_bls12381/pairing.rs b/circuit-std-rs/tests/gnark/emulated/sw_bls12381/pairing.rs index 51192af2..a85f1074 100644 --- a/circuit-std-rs/tests/gnark/emulated/sw_bls12381/pairing.rs +++ b/circuit-std-rs/tests/gnark/emulated/sw_bls12381/pairing.rs @@ -1,14 +1,16 @@ -use circuit_std_rs::gnark::{ - element::Element, - emulated::{ - field_bls12381::e2::GE2, - sw_bls12381::{g1::*, g2::*, pairing::Pairing}, +use circuit_std_rs::{ + gnark::{ + element::Element, + emulated::{ + field_bls12381::e2::GE2, + sw_bls12381::{g1::*, g2::*, pairing::Pairing}, + }, }, - hints::register_hint, + utils::register_hint, }; use expander_compiler::{ declare_circuit, - frontend::{extra::debug_eval, GenericDefine, HintRegistry, M31Config, RootAPI, Variable, M31}, + frontend::{extra::debug_eval, Define, HintRegistry, M31Config, RootAPI, Variable, M31}, }; declare_circuit!(PairingCheckGKRCircuit { @@ -18,7 +20,7 @@ declare_circuit!(PairingCheckGKRCircuit { in2_g2: [[[Variable; 48]; 2]; 2], }); -impl GenericDefine for PairingCheckGKRCircuit { +impl Define for PairingCheckGKRCircuit { fn define>(&self, builder: &mut Builder) { let mut pairing = Pairing::new(builder); let p1_g1 = G1Affine { diff --git a/circuit-std-rs/tests/logup.rs b/circuit-std-rs/tests/logup.rs index 14522286..87295e7a 100644 --- a/circuit-std-rs/tests/logup.rs +++ b/circuit-std-rs/tests/logup.rs @@ -21,7 +21,7 @@ fn logup_test() { } declare_circuit!(LogUpRangeproofCircuit { test: Variable }); -impl GenericDefine for LogUpRangeproofCircuit { +impl Define for LogUpRangeproofCircuit { fn define>(&self, builder: &mut Builder) { let mut table = LogUpRangeProofTable::new(8); table.initial(builder); @@ -45,7 +45,7 @@ fn rangeproof_logup_test() { hint_registry.register("myhint.querycounthint", query_count_hint); hint_registry.register("myhint.rangeproofhint", rangeproof_hint); //compile and test - let compile_result = compile_generic( + let compile_result = compile( &LogUpRangeproofCircuit::default(), CompileOptions::default(), ) diff --git a/circuit-std-rs/tests/matmul.rs b/circuit-std-rs/tests/matmul.rs new file mode 100644 index 00000000..daf830c4 --- /dev/null +++ b/circuit-std-rs/tests/matmul.rs @@ -0,0 +1,48 @@ +mod common; + +use circuit_std_rs::matmul::{MatMulCircuit, MatMulParams}; +use expander_compiler::frontend::*; + +#[test] +fn matmul_test() { + let matmul_params = [ + MatMulParams { + m1: 4, + n1: 3, + m2: 3, + n2: 2, + }, + MatMulParams { + m1: 6, + n1: 6, + m2: 6, + n2: 6, + }, + MatMulParams { + m1: 10, + n1: 5, + m2: 5, + n2: 1, + }, + MatMulParams { + m1: 1, + n1: 1, + m2: 1, + n2: 1, + }, + MatMulParams { + m1: 50, + n1: 35, + m2: 35, + n2: 65, + }, + ]; + + for params in matmul_params.iter() { + common::circuit_test_helper::(params); + common::circuit_test_helper::(params); + } + + //let mut rng = rand::rngs::StdRng::seed_from_u64(1235); + //debug_eval(&MatMulCircuit::default(), &MatMulCircuit::new_assignment(&matmul_params, rng), EmptyHintCaller); +} diff --git a/circuit-std-rs/tests/poseidon_m31.rs b/circuit-std-rs/tests/poseidon_m31.rs index 0faa5ae4..7d8a3ff8 100644 --- a/circuit-std-rs/tests/poseidon_m31.rs +++ b/circuit-std-rs/tests/poseidon_m31.rs @@ -1,4 +1,4 @@ -use circuit_std_rs::poseidon_m31::*; +use circuit_std_rs::{poseidon_m31::*, utils::register_hint}; use expander_compiler::frontend::*; declare_circuit!(PoseidonSpongeLen8Circuit { @@ -7,7 +7,7 @@ declare_circuit!(PoseidonSpongeLen8Circuit { }); impl Define for PoseidonSpongeLen8Circuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let params = PoseidonM31Params::new( builder, POSEIDON_M31X16_RATE, @@ -23,7 +23,11 @@ impl Define for PoseidonSpongeLen8Circuit { #[test] // NOTE(HS) Poseidon Mersenne-31 Width-16 Sponge tested over input length 8 fn test_poseidon_m31x16_hash_to_state_input_len8() { - let compile_result = compile(&PoseidonSpongeLen8Circuit::default()).unwrap(); + let compile_result = compile( + &PoseidonSpongeLen8Circuit::default(), + CompileOptions::default(), + ) + .unwrap(); let assignment = PoseidonSpongeLen8Circuit:: { inputs: [M31::from(114514); 8], @@ -60,7 +64,7 @@ declare_circuit!(PoseidonSpongeLen16Circuit { }); impl Define for PoseidonSpongeLen16Circuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let params = PoseidonM31Params::new( builder, POSEIDON_M31X16_RATE, @@ -68,7 +72,7 @@ impl Define for PoseidonSpongeLen16Circuit { POSEIDON_M31X16_FULL_ROUNDS, POSEIDON_M31X16_PARTIAL_ROUNDS, ); - let res = params.hash_to_state(builder, &self.inputs); + let res = params.hash_to_state_flatten(builder, &self.inputs); (0..params.width).for_each(|i| builder.assert_is_equal(res[i], self.outputs[i])); } } @@ -76,8 +80,13 @@ impl Define for PoseidonSpongeLen16Circuit { #[test] // NOTE(HS) Poseidon Mersenne-31 Width-16 Sponge tested over input length 16 fn test_poseidon_m31x16_hash_to_state_input_len16() { - let compile_result = compile(&PoseidonSpongeLen16Circuit::default()).unwrap(); - + let compile_result = compile( + &PoseidonSpongeLen16Circuit::default(), + CompileOptions::default(), + ) + .unwrap(); + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); let assignment = PoseidonSpongeLen16Circuit:: { inputs: [M31::from(114514); 16], outputs: [ @@ -101,7 +110,7 @@ fn test_poseidon_m31x16_hash_to_state_input_len16() { }; let witness = compile_result .witness_solver - .solve_witness(&assignment) + .solve_witness_with_hints(&assignment, &mut hint_registry) .unwrap(); let output = compile_result.layered_circuit.run(&witness); assert_eq!(output, vec![true]); diff --git a/circuit-std-rs/tests/sha256_gf2.rs b/circuit-std-rs/tests/sha256_gf2.rs index 4df1b4cf..e86c3a0c 100644 --- a/circuit-std-rs/tests/sha256_gf2.rs +++ b/circuit-std-rs/tests/sha256_gf2.rs @@ -16,7 +16,7 @@ declare_circuit!(SHA256CircuitCompressionOnly { output: [Variable; 256], }); -impl GenericDefine for SHA256CircuitCompressionOnly { +impl Define for SHA256CircuitCompressionOnly { fn define>(&self, api: &mut Builder) { let hasher = SHA256GF2::new(); let mut state = SHA256_INIT_STATE @@ -41,7 +41,7 @@ fn test_sha256_compression_gf2() { // ) // .unwrap(); - let compile_result = compile_generic_cross_layer( + let compile_result = compile_cross_layer( &SHA256CircuitCompressionOnly::default(), CompileOptions::default(), ) @@ -91,7 +91,7 @@ declare_circuit!(SHA256Circuit { output: [Variable; OUTPUT_LEN], }); -impl GenericDefine for SHA256Circuit { +impl Define for SHA256Circuit { fn define>(&self, api: &mut Builder) { let mut hasher = SHA256GF2::new(); hasher.update(&self.input); @@ -107,7 +107,7 @@ fn test_sha256_gf2() { // compile_generic(&SHA256Circuit::default(), CompileOptions::default()).unwrap(); let compile_result = - compile_generic_cross_layer(&SHA256Circuit::default(), CompileOptions::default()).unwrap(); + compile_cross_layer(&SHA256Circuit::default(), CompileOptions::default()).unwrap(); let n_tests = 5; let mut rng = rand::thread_rng(); diff --git a/circuit-std-rs/tests/sha256_m31.rs b/circuit-std-rs/tests/sha256_m31.rs index 028f8e9e..508a9133 100644 --- a/circuit-std-rs/tests/sha256_m31.rs +++ b/circuit-std-rs/tests/sha256_m31.rs @@ -20,7 +20,7 @@ pub fn check_sha256>( result } -impl GenericDefine for SHA25637BYTESCircuit { +impl Define for SHA25637BYTESCircuit { fn define>(&self, builder: &mut Builder) { for _ in 0..8 { let mut data = self.input.to_vec(); @@ -35,7 +35,7 @@ fn test_sha256_37bytes() { let mut hint_registry = HintRegistry::::new(); hint_registry.register("myhint.tobinary", to_binary_hint); let compile_result = - compile_generic(&SHA25637BYTESCircuit::default(), CompileOptions::default()).unwrap(); + compile(&SHA25637BYTESCircuit::default(), CompileOptions::default()).unwrap(); for i in 0..1 { let data = [i; 37]; let mut hash = Sha256::new(); diff --git a/efc/Cargo.toml b/efc/Cargo.toml new file mode 100644 index 00000000..3873cb09 --- /dev/null +++ b/efc/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "efc" +version = "0.1.0" +edition = "2021" + + +[dependencies] +expander_compiler = { path = "../expander_compiler"} +circuit-std-rs = { path = "../circuit-std-rs"} +hex = "0.4" +ark-std.workspace = true +rand.workspace = true +expander_config.workspace = true +expander_circuit.workspace = true +gkr.workspace = true +arith.workspace = true +gf2.workspace = true +mersenne31.workspace = true +ark-ec = "0.4.0" +ark-ff = "0.4.0" +ark-bls12-381 = "0.4.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +stacker = "0.1.17" +base64 = "0.22.1" +rayon = "1.10.0" +clap.workspace = true +num-traits = "0.2.19" +num-bigint = "0.4.6" +sha2 = "0.10.8" +mpi_config.workspace = true diff --git a/efc/readme.md b/efc/readme.md new file mode 100644 index 00000000..3a1b22ff --- /dev/null +++ b/efc/readme.md @@ -0,0 +1,16 @@ +### Introduction + +EthFullConsensus is a circuit library containing a series of circuits to realize Ethereum full consensus. The library can generate a series of circuit files. Given the target assignment data, the library can generate corresponding witness files for generating proof to prove that the supermajority of validators have the same target attestation, i.e., finalizing a source epoch, justifying a target epoch, and keeping the same beacon root. The layered circuit and witness files are used by the [Expander](https://github.com/PolyhedraZK/Expander) prover to generate proofs. + +### Concepts + +Realizing Ethereum full consensus using SNARKs can be costly. Our design is based on the concepts on [beacon-chain-validator](./spec/beacon-chain-validator.md). + +### Workflow + +1. Provide the assignment data files, and run the API to generate circuit.txt and witness.txt files +```RUSTFLAGS="-C target-cpu=native" cargo run --bin efc --release -- -d ``` +For example, if the assignment data files are on the "~/ExpanderCompilerCollection/efc/data", then run +```RUSTFLAGS="-C target-cpu=native" cargo run --bin efc --release -- -d ~/ExpanderCompilerCollection/efc/data``` +By default, the witness files are saved on the "~/ExpanderCompilerCollection/efc/witnesses". +2. Using Expander to provide the proofs, and verify them diff --git a/efc/src/attestation.rs b/efc/src/attestation.rs new file mode 100644 index 00000000..89db5d3a --- /dev/null +++ b/efc/src/attestation.rs @@ -0,0 +1,430 @@ +use circuit_std_rs::sha256::m31::sha256_var_bytes; +use expander_compiler::frontend::*; +use serde::Deserialize; + +const ZERO_HASHES: [&[u8]; 40] = [ + &[0; 32], + &[ + 245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, 61, 35, + 32, 217, 240, 232, 234, 152, 49, 169, 39, 89, 251, 75, + ], + &[ + 219, 86, 17, 78, 0, 253, 212, 193, 248, 92, 137, 43, 243, 90, 201, 168, 146, 137, 170, 236, + 177, 235, 208, 169, 108, 222, 96, 106, 116, 139, 93, 113, + ], + &[ + 199, 128, 9, 253, 240, 127, 197, 106, 17, 241, 34, 55, 6, 88, 163, 83, 170, 165, 66, 237, + 99, 228, 76, 75, 193, 95, 244, 205, 16, 90, 179, 60, + ], + &[ + 83, 109, 152, 131, 127, 45, 209, 101, 165, 93, 94, 234, 233, 20, 133, 149, 68, 114, 213, + 111, 36, 109, 242, 86, 191, 60, 174, 25, 53, 42, 18, 60, + ], + &[ + 158, 253, 224, 82, 170, 21, 66, 159, 174, 5, 186, 212, 208, 177, 215, 198, 77, 166, 77, 3, + 215, 161, 133, 74, 88, 140, 44, 184, 67, 12, 13, 48, + ], + &[ + 216, 141, 223, 238, 212, 0, 168, 117, 85, 150, 178, 25, 66, 193, 73, 126, 17, 76, 48, 46, + 97, 24, 41, 15, 145, 230, 119, 41, 118, 4, 31, 161, + ], + &[ + 135, 235, 13, 219, 165, 126, 53, 246, 210, 134, 103, 56, 2, 164, 175, 89, 117, 226, 37, 6, + 199, 207, 76, 100, 187, 107, 229, 238, 17, 82, 127, 44, + ], + &[ + 38, 132, 100, 118, 253, 95, 197, 74, 93, 67, 56, 81, 103, 201, 81, 68, 242, 100, 63, 83, + 60, 200, 91, 185, 209, 107, 120, 47, 141, 125, 177, 147, + ], + &[ + 80, 109, 134, 88, 45, 37, 36, 5, 184, 64, 1, 135, 146, 202, 210, 191, 18, 89, 241, 239, 90, + 165, 248, 135, 225, 60, 178, 240, 9, 79, 81, 225, + ], + &[ + 255, 255, 10, 215, 230, 89, 119, 47, 149, 52, 193, 149, 200, 21, 239, 196, 1, 78, 241, 225, + 218, 237, 68, 4, 192, 99, 133, 209, 17, 146, 233, 43, + ], + &[ + 108, 240, 65, 39, 219, 5, 68, 28, 216, 51, 16, 122, 82, 190, 133, 40, 104, 137, 14, 67, 23, + 230, 160, 42, 180, 118, 131, 170, 117, 150, 66, 32, + ], + &[ + 183, 208, 95, 135, 95, 20, 0, 39, 239, 81, 24, 162, 36, 123, 187, 132, 206, 143, 47, 15, + 17, 35, 98, 48, 133, 218, 247, 150, 12, 50, 159, 95, + ], + &[ + 223, 106, 245, 245, 187, 219, 107, 233, 239, 138, 166, 24, 228, 191, 128, 115, 150, 8, 103, + 23, 30, 41, 103, 111, 139, 40, 77, 234, 106, 8, 168, 94, + ], + &[ + 181, 141, 144, 15, 94, 24, 46, 60, 80, 239, 116, 150, 158, 161, 108, 119, 38, 197, 73, 117, + 124, 194, 53, 35, 195, 105, 88, 125, 167, 41, 55, 132, + ], + &[ + 212, 154, 117, 2, 255, 207, 176, 52, 11, 29, 120, 133, 104, 133, 0, 202, 48, 129, 97, 167, + 249, 107, 98, 223, 157, 8, 59, 113, 252, 200, 242, 187, + ], + &[ + 143, 230, 177, 104, 146, 86, 192, 211, 133, 244, 47, 91, 190, 32, 39, 162, 44, 25, 150, + 225, 16, 186, 151, 193, 113, 211, 229, 148, 141, 233, 43, 235, + ], + &[ + 141, 13, 99, 195, 158, 186, 222, 133, 9, 224, 174, 60, 156, 56, 118, 251, 95, 161, 18, 190, + 24, 249, 5, 236, 172, 254, 203, 146, 5, 118, 3, 171, + ], + &[ + 149, 238, 200, 178, 229, 65, 202, 212, 233, 29, 227, 131, 133, 242, 224, 70, 97, 159, 84, + 73, 108, 35, 130, 203, 108, 172, 213, 185, 140, 38, 245, 164, + ], + &[ + 248, 147, 233, 8, 145, 119, 117, 182, 43, 255, 35, 41, 77, 187, 227, 161, 205, 142, 108, + 193, 195, 91, 72, 1, 136, 123, 100, 106, 111, 129, 241, 127, + ], + &[ + 205, 219, 167, 181, 146, 227, 19, 51, 147, 193, 97, 148, 250, 199, 67, 26, 191, 47, 84, + 133, 237, 113, 29, 178, 130, 24, 60, 129, 158, 8, 235, 170, + ], + &[ + 138, 141, 127, 227, 175, 140, 170, 8, 90, 118, 57, 168, 50, 0, 20, 87, 223, 185, 18, 138, + 128, 97, 20, 42, 208, 51, 86, 41, 255, 35, 255, 156, + ], + &[ + 254, 179, 195, 55, 215, 165, 26, 111, 191, 0, 185, 227, 76, 82, 225, 201, 25, 92, 150, 155, + 212, 231, 160, 191, 213, 29, 92, 91, 237, 156, 17, 103, + ], + &[ + 231, 31, 10, 168, 60, 195, 46, 223, 190, 250, 159, 77, 62, 1, 116, 202, 133, 24, 46, 236, + 159, 58, 9, 246, 166, 192, 223, 99, 119, 165, 16, 215, + ], + &[ + 49, 32, 111, 168, 10, 80, 187, 106, 190, 41, 8, 80, 88, 241, 98, 18, 33, 42, 96, 238, 200, + 240, 73, 254, 203, 146, 216, 200, 224, 168, 75, 192, + ], + &[ + 33, 53, 43, 254, 203, 237, 221, 233, 147, 131, 159, 97, 76, 61, 172, 10, 62, 227, 117, 67, + 249, 180, 18, 177, 97, 153, 220, 21, 142, 35, 181, 68, + ], + &[ + 97, 158, 49, 39, 36, 187, 109, 124, 49, 83, 237, 157, 231, 145, 215, 100, 163, 102, 179, + 137, 175, 19, 197, 139, 248, 168, 217, 4, 129, 164, 103, 101, + ], + &[ + 124, 221, 41, 134, 38, 130, 80, 98, 141, 12, 16, 227, 133, 197, 140, 97, 145, 230, 251, + 224, 81, 145, 188, 192, 79, 19, 63, 44, 234, 114, 193, 196, + ], + &[ + 132, 137, 48, 189, 123, 168, 202, 197, 70, 97, 7, 33, 19, 251, 39, 136, 105, 224, 123, 184, + 88, 127, 145, 57, 41, 51, 55, 77, 1, 123, 203, 225, + ], + &[ + 136, 105, 255, 44, 34, 178, 140, 193, 5, 16, 217, 133, 50, 146, 128, 51, 40, 190, 79, 176, + 232, 4, 149, 232, 187, 141, 39, 31, 91, 136, 150, 54, + ], + &[ + 181, 254, 40, 231, 159, 27, 133, 15, 134, 88, 36, 108, 233, 182, 161, 231, 180, 159, 192, + 109, 183, 20, 62, 143, 224, 180, 242, 176, 197, 82, 58, 92, + ], + &[ + 152, 94, 146, 159, 112, 175, 40, 208, 189, 209, 169, 10, 128, 143, 151, 127, 89, 124, 124, + 119, 140, 72, 158, 152, 211, 189, 137, 16, 211, 26, 192, 247, + ], + &[ + 198, 246, 126, 2, 230, 228, 225, 189, 239, 185, 148, 198, 9, 137, 83, 243, 70, 54, 186, 43, + 108, 162, 10, 71, 33, 210, 178, 106, 136, 103, 34, 255, + ], + &[ + 28, 154, 126, 95, 241, 207, 72, 180, 173, 21, 130, 211, 244, 228, 161, 0, 79, 59, 32, 216, + 197, 162, 183, 19, 135, 164, 37, 74, 217, 51, 235, 197, + ], + &[ + 47, 7, 90, 226, 41, 100, 107, 111, 106, 237, 25, 165, 227, 114, 207, 41, 80, 129, 64, 30, + 184, 147, 255, 89, 155, 63, 154, 204, 12, 13, 62, 125, + ], + &[ + 50, 137, 33, 222, 181, 150, 18, 7, 104, 1, 232, 205, 97, 89, 33, 7, 181, 198, 124, 121, + 184, 70, 89, 92, 198, 50, 12, 57, 91, 70, 54, 44, + ], + &[ + 191, 185, 9, 253, 178, 54, 173, 36, 17, 180, 228, 136, 56, 16, 160, 116, 184, 64, 70, 70, + 137, 152, 108, 63, 138, 128, 145, 130, 126, 23, 195, 39, + ], + &[ + 85, 216, 251, 54, 135, 186, 59, 164, 159, 52, 44, 119, 245, 161, 248, 155, 236, 131, 216, + 17, 68, 110, 26, 70, 113, 57, 33, 61, 100, 11, 106, 116, + ], + &[ + 247, 33, 13, 79, 142, 126, 16, 57, 121, 14, 123, 244, 239, 162, 7, 85, 90, 16, 166, 219, + 29, 212, 185, 93, 163, 19, 170, 168, 139, 136, 254, 118, + ], + &[ + 173, 33, 181, 22, 203, 198, 69, 255, 227, 74, 181, 222, 28, 138, 239, 140, 212, 231, 248, + 210, 181, 30, 142, 20, 86, 173, 199, 86, 60, 218, 32, 111, + ], +]; + +#[derive(Debug, Deserialize, Clone)] +pub struct CheckpointPlain { + pub epoch: u64, + pub root: String, +} +#[derive(Debug, Deserialize, Clone)] +pub struct AttestationData { + #[serde(default)] + pub slot: u64, + #[serde(default)] + pub committee_index: u64, + pub beacon_block_root: String, + pub source: CheckpointPlain, + pub target: CheckpointPlain, +} +#[derive(Debug, Deserialize, Clone)] +pub struct Attestation { + #[serde(default)] + pub aggregation_bits: String, + pub data: AttestationData, + pub signature: String, +} + +#[derive(Default, Clone, Copy)] +pub struct AttestationDataSSZ { + pub slot: [Variable; 8], + pub committee_index: [Variable; 8], + pub beacon_block_root: [Variable; 32], + pub source_epoch: [Variable; 8], + pub target_epoch: [Variable; 8], + pub source_root: [Variable; 32], + pub target_root: [Variable; 32], +} +impl AttestationDataSSZ { + pub fn new() -> Self { + Self { + slot: [Variable::default(); 8], + committee_index: [Variable::default(); 8], + beacon_block_root: [Variable::default(); 32], + source_epoch: [Variable::default(); 8], + target_epoch: [Variable::default(); 8], + source_root: [Variable::default(); 32], + target_root: [Variable::default(); 32], + } + } + pub fn att_data_signing_root>( + &self, + builder: &mut B, + att_domain: &[Variable], + ) -> Vec { + let att_data_hash_tree_root = self.hash_tree_root(builder); + bytes_hash_tree_root( + builder, + [att_data_hash_tree_root, att_domain.to_vec()].concat(), + ) + } + + pub fn check_point_hash_tree_variable>( + &self, + builder: &mut B, + epoch: &[Variable], + root: &[Variable], + ) -> Vec { + let mut inputs = Vec::new(); + inputs.extend_from_slice(&append_to_32_bytes(builder, epoch)); + inputs.extend_from_slice(root); + bytes_hash_tree_root(builder, inputs) + } + pub fn hash_tree_root>(&self, builder: &mut B) -> Vec { + let mut inputs = Vec::new(); + inputs.extend_from_slice(&append_to_32_bytes(builder, &self.slot)); + inputs.extend_from_slice(&append_to_32_bytes(builder, &self.committee_index)); + inputs.extend_from_slice(&self.beacon_block_root); + let source_checkpoint_root = + self.check_point_hash_tree_variable(builder, &self.source_epoch, &self.source_root); + inputs.extend_from_slice(&source_checkpoint_root); + let target_checkpoint_root = + self.check_point_hash_tree_variable(builder, &self.target_epoch, &self.target_root); + inputs.extend_from_slice(&target_checkpoint_root); + bytes_hash_tree_root(builder, inputs) + } +} +pub fn bytes_hash_tree_root>( + builder: &mut B, + inputs: Vec, +) -> Vec { + let chunks = to_chunks(&append_to_32_bytes(builder, &inputs)); + beacon_merklize(builder, chunks).unwrap() +} +pub fn beacon_merklize>( + builder: &mut B, + inputs: Vec>, +) -> Result, String> { + if inputs.is_empty() { + return Err("no inputs".to_string()); + } + if inputs.len() == 1 { + return Ok(inputs[0].clone()); + } + let mut length = inputs.len(); + let depth = (length as f64).log2().ceil() as usize; + let mut inputs = inputs; + for padding_hash in ZERO_HASHES.iter().take(depth) { + if inputs.len() % 2 == 1 { + let pad_hash = *padding_hash; + let padding: Vec<_> = pad_hash + .iter() + .map(|&x| builder.constant(x as u32)) + .collect(); + inputs.push(padding); + } + let mut new_level = Vec::new(); + for j in (0..length).step_by(2) { + let mut combined = vec![]; + combined.extend_from_slice(&inputs[j]); + combined.extend_from_slice(&inputs[j + 1]); + let hash = sha256_var_bytes(builder, &combined); + new_level.push(hash); + } + inputs = new_level; + length = inputs.len(); + } + Ok(inputs[0].clone()) +} + +pub fn append_to_32_bytes>( + builder: &mut B, + input: &[Variable], +) -> Vec { + let rest = input.len() % 32; + if rest != 0 { + let padding = vec![builder.constant(0); 32 - rest]; + let mut input = input.to_vec(); + input.extend_from_slice(&padding); + input + } else { + input.to_vec() + } +} + +pub fn to_chunks(input: &[Variable]) -> Vec> { + if input.len() % 32 != 0 { + panic!("input length is not a multiple of 32"); + } + input.chunks(32).map(|x| x.to_vec()).collect() +} + +declare_circuit!(AttHashCircuit { + //AttestationSSZ + slot: [Variable; 8], + committee_index: [Variable; 8], + beacon_beacon_block_root: [Variable; 32], + source_epoch: [Variable; 8], + target_epoch: [Variable; 8], + source_root: [Variable; 32], + target_root: [Variable; 32], + //att_domain + domain: [Variable; 32], + //att_signing_hash + outputs: [Variable; 32], +}); + +impl Define for AttHashCircuit { + fn define>(&self, builder: &mut Builder) { + let att_ssz = AttestationDataSSZ { + slot: self.slot, + committee_index: self.committee_index, + beacon_block_root: self.beacon_beacon_block_root, + source_epoch: self.source_epoch, + target_epoch: self.target_epoch, + source_root: self.source_root, + target_root: self.target_root, + }; + let att_hash = att_ssz.att_data_signing_root(builder, &self.domain); + for (i, att_hash_byte) in att_hash.iter().enumerate().take(32) { + builder.assert_is_equal(att_hash_byte, self.outputs[i]); + } + } +} +#[cfg(test)] +mod tests { + // use crate::{attestation::Attestation, utils::read_from_json_file}; + + use super::AttHashCircuit; + use circuit_std_rs::utils::register_hint; + use expander_compiler::frontend::*; + use extra::debug_eval; + #[test] + fn test_attestation_hash() { + // att.Data.Slot 9280000 + // att.Data.CommitteeIndex 0 + // att.Data.BeaconBlockRoot [31 28 22 87 106 251 75 169 100 167 224 201 6 63 144 105 213 235 18 224 169 157 122 56 47 48 28 31 124 69 38 248] + // att.Data.Source 289999 [194 212 152 232 56 145 101 103 73 230 240 242 89 129 63 184 38 157 86 185 251 148 157 68 227 144 241 74 228 200 206 199] + // att.Data.Target 290000 [31 28 22 87 106 251 75 169 100 167 224 201 6 63 144 105 213 235 18 224 169 157 122 56 47 48 28 31 124 69 38 248] + // att.Signature [170 121 191 2 187 22 51 113 109 233 89 181 237 140 207 117 72 230 115 61 124 161 23 145 241 245 211 134 175 182 206 188 124 240 51 154 121 27 217 24 126 83 70 24 90 206 50 148 2 182 65 209 6 215 131 231 254 32 229 193 207 91 52 22 89 10 212 80 4 160 179 150 246 97 120 81 28 231 36 195 223 118 194 250 230 31 182 130 163 236 45 222 26 229 163 89] + // msg: [21 43 211 145 56 110 228 123 66 36 151 4 255 189 148 168 249 77 23 127 110 62 89 50 240 62 155 2 139 217 153 140] + // domain: [1 0 0 0 187 164 218 150 53 76 159 37 71 108 241 188 105 191 88 58 127 158 10 240 73 48 91 98 222 103 102 64] + // msgList[ 0 ]: [108 128 22 84 10 154 231 122 105 134 112 241 41 75 92 55 89 54 23 5 113 63 35 4 32 197 151 179 250 27 66 13] + // sigList[ 0 ]: E([417406042303837766676050444382954581819710384023930335899613364000243943316124744931107291428889984115562657456985+1612337918776384379710682981548399375489832112491603419994252758241488024847803823620674751718035900645102653944468*u,2138372746384454686692156684769748785619173944336480358459807585988147682623523096063056865298570471165754367761702+2515621099638397509480666850964364949449167540660259026336903510150090825582288208580180650995842554224706524936338*u]) + + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = AttHashCircuit:: { + slot: [M31::from(0); 8], + committee_index: [M31::from(0); 8], + beacon_beacon_block_root: [M31::from(0); 32], + source_epoch: [M31::from(0); 8], + target_epoch: [M31::from(0); 8], + source_root: [M31::from(0); 32], + target_root: [M31::from(0); 32], + domain: [M31::from(0); 32], + outputs: [M31::from(0); 32], + }; + let slot: u64 = 9280000; + let slot = slot.to_le_bytes(); + let committee_index: u64 = 0; + let committee_index = committee_index.to_le_bytes(); + let beacon_beacon_block_root = vec![ + 31, 28, 22, 87, 106, 251, 75, 169, 100, 167, 224, 201, 6, 63, 144, 105, 213, 235, 18, + 224, 169, 157, 122, 56, 47, 48, 28, 31, 124, 69, 38, 248, + ]; + let source_epoch: u64 = 289999; + let source_epoch = source_epoch.to_le_bytes(); + let target_epoch: u64 = 290000; + let target_epoch = target_epoch.to_le_bytes(); + let source_root = vec![ + 194, 212, 152, 232, 56, 145, 101, 103, 73, 230, 240, 242, 89, 129, 63, 184, 38, 157, + 86, 185, 251, 148, 157, 68, 227, 144, 241, 74, 228, 200, 206, 199, + ]; + let target_root = vec![ + 31, 28, 22, 87, 106, 251, 75, 169, 100, 167, 224, 201, 6, 63, 144, 105, 213, 235, 18, + 224, 169, 157, 122, 56, 47, 48, 28, 31, 124, 69, 38, 248, + ]; + let domain = vec![ + 1, 0, 0, 0, 187, 164, 218, 150, 53, 76, 159, 37, 71, 108, 241, 188, 105, 191, 88, 58, + 127, 158, 10, 240, 73, 48, 91, 98, 222, 103, 102, 64, + ]; + let output = vec![ + 108, 128, 22, 84, 10, 154, 231, 122, 105, 134, 112, 241, 41, 75, 92, 55, 89, 54, 23, 5, + 113, 63, 35, 4, 32, 197, 151, 179, 250, 27, 66, 13, + ]; + + for i in 0..8 { + assignment.slot[i] = M31::from(slot[i] as u32); + assignment.committee_index[i] = M31::from(committee_index[i] as u32); + assignment.source_epoch[i] = M31::from(source_epoch[i] as u32); + assignment.target_epoch[i] = M31::from(target_epoch[i] as u32); + } + for i in 0..32 { + assignment.beacon_beacon_block_root[i] = M31::from(beacon_beacon_block_root[i] as u32); + assignment.source_root[i] = M31::from(source_root[i] as u32); + assignment.target_root[i] = M31::from(target_root[i] as u32); + assignment.domain[i] = M31::from(domain[i] as u32); + assignment.outputs[i] = M31::from(output[i] as u32); + } + + debug_eval(&AttHashCircuit::default(), &assignment, hint_registry); + } + + // #[test] + // fn read_attestation() { + // let file_path = "./data/slotAttestationsFolded.json"; + // let attestations: Vec = read_from_json_file(file_path).unwrap(); + // println!("attestations[0]:{:?}", attestations[0]); + // } +} diff --git a/efc/src/bls.rs b/efc/src/bls.rs new file mode 100644 index 00000000..2f22acfe --- /dev/null +++ b/efc/src/bls.rs @@ -0,0 +1,198 @@ +use circuit_std_rs::gnark::emulated::sw_bls12381::g1::G1Affine; +use circuit_std_rs::sha256::m31_utils::{ + big_is_zero, big_less_than, bigint_to_m31_array, to_binary, +}; +use circuit_std_rs::utils::{simple_lookup2, simple_select}; +use expander_compiler::frontend::*; +use num_bigint::BigInt; +use std::str::FromStr; + +const K: usize = 48; +const N: usize = 8; +const M_COMPRESSED_SMALLEST: u8 = 0b100 << 5; +const M_COMPRESSED_LARGEST: u8 = 0b101 << 5; +const M_COMPRESSED_INFINITY: u8 = 0b110 << 5; + +pub fn convert_to_public_key_bls>( + api: &mut B, + pubkey: Vec, +) -> (G1Affine, Variable) { + let mut empty_flag = api.constant(1); //if pubkey is empty (all -1), emptyFlag = 1 + for _ in 0..pubkey.len() { + let tmp = api.add(pubkey[0], 1); + let flag = api.is_zero(tmp); + empty_flag = api.and(empty_flag, flag); //if pubkey is not empty, emptyFlag = 0 + } + let mut inputs = pubkey.clone(); + inputs.insert(0, empty_flag); + //use a hint to get the bls publickey + let outputs = api.new_hint("getPublicKeyBLSHint", &inputs, pubkey.len() * 2); + let public_key_bls = G1Affine::from_vars(outputs[0..K].to_vec(), outputs[K..2 * K].to_vec()); + let logup_var = assert_public_key_and_bls(api, pubkey, &public_key_bls, empty_flag); + + (public_key_bls, logup_var) +} + +pub fn check_pubkey_key_bls>( + api: &mut B, + pubkey: Vec, + public_key_bls: &G1Affine, +) -> Variable { + let empty_flag = api.constant(0); + assert_public_key_and_bls(api, pubkey, public_key_bls, empty_flag) +} + +pub fn assert_public_key_and_bls>( + api: &mut B, + pubkey: Vec, + public_key_bls: &G1Affine, + empty_flag: Variable, +) -> Variable { + let x_is_zero = big_is_zero(api, K, &public_key_bls.x.limbs); + let y_is_zero = big_is_zero(api, K, &public_key_bls.y.limbs); + let is_infinity = api.mul(x_is_zero, y_is_zero); + + let half_fp = BigInt::from_str("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787").unwrap() / 2; + let half_fp_var = bigint_to_m31_array(api, half_fp, N, K); + let lex_large = big_less_than(api, N, K, &half_fp_var, &public_key_bls.y.limbs); + // + // 0 0: mCompressedSmallest + // 1 0: mCompressedInfinity + // 0 1: mCompressedLargest + // 1 1: 0 + let m_compressed_infinity_var = api.constant(M_COMPRESSED_INFINITY as u32); + let m_compressed_smallest_var = api.constant(M_COMPRESSED_SMALLEST as u32); + let m_compressed_largest_var = api.constant(M_COMPRESSED_LARGEST as u32); + let zero_var = api.constant(0); + let mask = simple_lookup2( + api, + is_infinity, + lex_large, + m_compressed_smallest_var, + m_compressed_infinity_var, + m_compressed_largest_var, + zero_var, + ); + + let mut out_tmp = pubkey.clone(); + out_tmp[0] = api.sub(out_tmp[0], mask); + // logup::range_proof_single_chunk(api, out_tmp[0], 5); //return the value, and logup it to the range of 5 after this function call + compare_two_scalars(api, &public_key_bls.x.limbs, N, &out_tmp, 8, empty_flag); + out_tmp[0] +} +pub fn compare_two_scalars>( + api: &mut B, + scalar1: &[Variable], + n_bit1: usize, + scalar2: &[Variable], + n_bit2: usize, + empty_flag: Variable, +) { + //first, we need to check the length of the field, i.e., m31 = 31 bits, bn254 = 254 bits + //we can compose scalar1 and scalar2 to bigInts, but they should have a length less than the field length + let available_bits = 31 - 1; + //Now, find a best way to compose scalar1 and scalar2 to bigInts + let gcd_n_bit1_n_bit2 = lcm_int(n_bit1, n_bit2); + let max_bits = scalar1.len() * n_bit1; + let expansion = + (max_bits / gcd_n_bit1_n_bit2) / ((max_bits + available_bits - 1) / available_bits); + if expansion == 0 { + //means the lcm is still too large, let's compare two scalars bit-by-bit + let scalar1_bits = decompose_vars(api, scalar1, n_bit1); + let scalar2_bits = decompose_vars(api, scalar2, n_bit2); + assert_eq!(scalar1_bits.len(), scalar2_bits.len()); + for i in 0..scalar1_bits.len() { + api.assert_is_equal(scalar1_bits[i], scalar2_bits[i]); + } + } else { + let target_bits = expansion * gcd_n_bit1_n_bit2; //we will compose the scalar1 and scalar2 to bigInts with targetBits + let chunk1_len = target_bits / n_bit1; + let mut scalar1_big = vec![api.constant(0); scalar1.len() / chunk1_len]; + for i in 0..scalar1_big.len() { + scalar1_big[i] = + compose_var_little(api, &scalar1[i * chunk1_len..(i + 1) * chunk1_len], n_bit1); + } + let chunk2_len = target_bits / n_bit2; + let mut scalar2_big = vec![api.constant(0); scalar2.len() / chunk2_len]; + for i in 0..scalar2_big.len() { + scalar2_big[i] = + compose_var_big(api, &scalar2[i * chunk2_len..(i + 1) * chunk2_len], n_bit2); + } + + //the length of scalar1Big and scalar2Big should be the same + assert_eq!(scalar1_big.len(), scalar2_big.len()); + //scalar1Big and scalar2Big should be the same + let scalar_big_len = scalar1_big.len(); + for i in 0..scalar_big_len { + scalar1_big[i] = simple_select( + api, + empty_flag, + scalar2_big[scalar_big_len - i - 1], + scalar1_big[i], + ); + + api.assert_is_equal(scalar1_big[i], scalar2_big[scalar_big_len - i - 1]); + } + } +} + +fn gcd(a: usize, b: usize) -> usize { + let mut a = a; + let mut b = b; + while b != 0 { + let tmp = a; + a = b; + b = tmp % b; + } + a +} +fn lcm_int(a: usize, b: usize) -> usize { + (a * b) / gcd(a, b) +} + +pub fn compose_var_little>( + api: &mut B, + scalar: &[Variable], + n_bit: usize, +) -> Variable { + if scalar.len() == 1 { + return scalar[0]; + } + //compose the scalar to a bigInt + let scalar_len = scalar.len(); + let mut scalar_big = scalar[scalar_len - 1]; + for i in 1..scalar_len { + scalar_big = api.mul(scalar_big, 1 << n_bit); + scalar_big = api.add(scalar_big, scalar[scalar_len - i - 1]); + } + scalar_big +} +pub fn compose_var_big>( + api: &mut B, + scalar: &[Variable], + n_bit: usize, +) -> Variable { + if scalar.len() == 1 { + return scalar[0]; + } + //compose the scalar to a bigInt + let scalar_len = scalar.len(); + let mut scalar_big = scalar[0]; + for scalar_byte in scalar.iter().take(scalar_len).skip(1) { + scalar_big = api.mul(scalar_big, 1 << n_bit); + scalar_big = api.add(scalar_big, scalar_byte); + } + scalar_big +} +pub fn decompose_vars>( + api: &mut B, + scalar: &[Variable], + n_bit: usize, +) -> Vec { + //decompose the scalar to a []big.Int + let mut scalar_array = vec![]; + for scalar_byte in scalar { + scalar_array.extend(to_binary(api, *scalar_byte, n_bit)); + } + scalar_array +} diff --git a/efc/src/bls_verifier.rs b/efc/src/bls_verifier.rs new file mode 100644 index 00000000..9bcdde5c --- /dev/null +++ b/efc/src/bls_verifier.rs @@ -0,0 +1,284 @@ +use std::sync::Arc; +use std::thread; + +use circuit_std_rs::gnark::emulated::sw_bls12381::g1::*; +use circuit_std_rs::gnark::emulated::sw_bls12381::g2::*; +use circuit_std_rs::gnark::emulated::sw_bls12381::pairing::*; +use circuit_std_rs::utils::register_hint; +use expander_compiler::circuit::ir::hint_normalized::witness_solver; +use expander_compiler::compile::CompileOptions; +use expander_compiler::declare_circuit; +use expander_compiler::frontend::compile; +use expander_compiler::frontend::internal::Serde; +use expander_compiler::frontend::Define; +use expander_compiler::frontend::HintRegistry; +use expander_compiler::frontend::M31Config; +use expander_compiler::frontend::{RootAPI, Variable, M31}; + +use serde::Deserialize; + +use crate::utils::ensure_directory_exists; +use crate::utils::read_from_json_file; + +#[derive(Clone, Debug, Deserialize)] +pub struct Limbs { + #[serde(rename = "Limbs")] + pub limbs: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Coordinate { + #[serde(rename = "A0")] + pub a0: Limbs, + #[serde(rename = "A1")] + pub a1: Limbs, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Point { + #[serde(rename = "X")] + pub x: Coordinate, + #[serde(rename = "Y")] + pub y: Coordinate, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct G2Json { + #[serde(rename = "P")] + pub p: Point, + #[serde(rename = "Lines")] + pub lines: Option>, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct G1Json { + #[serde(rename = "X")] + pub x: Limbs, + #[serde(rename = "Y")] + pub y: Limbs, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct PairingEntry { + #[serde(rename = "Hm")] + pub hm: G2Json, + #[serde(rename = "PubKey")] + pub pub_key: G1Json, + #[serde(rename = "Signature")] + pub signature: G2Json, +} + +declare_circuit!(PairingCircuit { + pubkey: [[Variable; 48]; 2], + hm: [[[Variable; 48]; 2]; 2], + sig: [[[Variable; 48]; 2]; 2] +}); + +pub fn convert_limbs(limbs: Vec) -> [M31; 48] { + let converted: Vec = limbs.into_iter().map(|x| M31::from(x as u32)).collect(); + converted.try_into().expect("Limbs should have 48 elements") +} + +pub fn convert_point(point: Coordinate) -> [[M31; 48]; 2] { + [convert_limbs(point.a0.limbs), convert_limbs(point.a1.limbs)] +} +impl PairingCircuit { + pub fn from_entry(entry: &PairingEntry) -> Self { + PairingCircuit { + pubkey: [ + convert_limbs(entry.pub_key.x.limbs.clone()), + convert_limbs(entry.pub_key.y.limbs.clone()), + ], + hm: [ + convert_point(entry.hm.p.x.clone()), + convert_point(entry.hm.p.y.clone()), + ], + sig: [ + convert_point(entry.signature.p.x.clone()), + convert_point(entry.signature.p.y.clone()), + ], + } + } +} +impl Define for PairingCircuit { + fn define>(&self, builder: &mut Builder) { + let mut pairing = Pairing::new(builder); + let one_g1 = G1Affine::one(builder); + let pubkey_g1 = G1Affine::from_vars(self.pubkey[0].to_vec(), self.pubkey[1].to_vec()); + let hm_g2 = G2AffP::from_vars( + self.hm[0][0].to_vec(), + self.hm[0][1].to_vec(), + self.hm[1][0].to_vec(), + self.hm[1][1].to_vec(), + ); + let sig_g2 = G2AffP::from_vars( + self.sig[0][0].to_vec(), + self.sig[0][1].to_vec(), + self.sig[1][0].to_vec(), + self.sig[1][1].to_vec(), + ); + + let mut g2 = G2::new(builder); + let neg_sig_g2 = g2.neg(builder, &sig_g2); + + let p_array = vec![one_g1, pubkey_g1]; + let mut q_array = [ + G2Affine { + p: neg_sig_g2, + lines: LineEvaluations::default(), + }, + G2Affine { + p: hm_g2, + lines: LineEvaluations::default(), + }, + ]; + pairing + .pairing_check(builder, &p_array, &mut q_array) + .unwrap(); + pairing.ext12.ext6.ext2.curve_f.check_mul(builder); + pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); + pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); + pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); + } +} + +pub fn generate_pairing_witnesses(dir: &str) { + println!("preparing solver..."); + ensure_directory_exists("./witnesses/pairing"); + let file_name = "pairing.witness"; + let w_s = if std::fs::metadata(file_name).is_ok() { + println!("The solver exists!"); + witness_solver::WitnessSolver::deserialize_from(std::fs::File::open(file_name).unwrap()) + .unwrap() + } else { + println!("The solver does not exist."); + let compile_result = + compile(&PairingCircuit::default(), CompileOptions::default()).unwrap(); + compile_result + .witness_solver + .serialize_into(std::fs::File::create(file_name).unwrap()) + .unwrap(); + compile_result.witness_solver + }; + + println!("Start generating witnesses..."); + let start_time = std::time::Instant::now(); + let file_path = format!("{}/pairing_assignment.json", dir); + + let pairing_data: Vec = read_from_json_file(&file_path).unwrap(); + let end_time = std::time::Instant::now(); + println!( + "loaded pairing data time: {:?}", + end_time.duration_since(start_time) + ); + let mut assignments = vec![]; + for cur_pairing_data in &pairing_data { + let pairing_assignment = PairingCircuit::from_entry(cur_pairing_data); + assignments.push(pairing_assignment); + } + let end_time = std::time::Instant::now(); + println!( + "assigned assignments time: {:?}", + end_time.duration_since(start_time) + ); + let assignment_chunks: Vec>> = + assignments.chunks(16).map(|x| x.to_vec()).collect(); + let witness_solver = Arc::new(w_s); + let handles = assignment_chunks + .into_iter() + .enumerate() + .map(|(i, assignments)| { + let witness_solver = Arc::clone(&witness_solver); + thread::spawn(move || { + let mut hint_registry1 = HintRegistry::::new(); + register_hint(&mut hint_registry1); + let witness = witness_solver + .solve_witnesses_with_hints(&assignments, &mut hint_registry1) + .unwrap(); + let file_name = format!("./witnesses/pairing/witness_{}.txt", i); + let file = std::fs::File::create(file_name).unwrap(); + let writer = std::io::BufWriter::new(file); + witness.serialize_into(writer).unwrap(); + }) + }) + .collect::>(); + for handle in handles { + handle.join().unwrap(); + } + let end_time = std::time::Instant::now(); + println!( + "Generate pairing witness Time: {:?}", + end_time.duration_since(start_time) + ); +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::utils::ensure_directory_exists; +// use std::fs::File; +// use std::io::Write; + +// declare_circuit!(VerifySigCircuit { +// pubkey: [[Variable; 48]; 2], +// slot: [Variable; 8], +// committee_index: [Variable; 8], +// beacon_block_root: [[Variable; 8]; 32], +// source_epoch: [Variable; 8], +// target_epoch: [Variable; 8], +// source_root: [Variable; 32], +// target_root: [Variable; 32], +// sig_byte: [Variable; 48] +// }); + +// impl Define for VerifySigCircuit { +// fn define>(&self, builder: &mut Builder) { +// let mut pairing = Pairing::new(builder); +// let one_g1 = G1Affine::one(builder); +// let pubkey_g1 = G1Affine::from_vars(self.pubkey[0].to_vec(), self.pubkey[1].to_vec()); +// let sig_g2 = G2AffP::from_vars( +// self.sig[0][0].to_vec(), +// self.sig[0][1].to_vec(), +// self.sig[1][0].to_vec(), +// self.sig[1][1].to_vec(), +// ); + +// let mut g2 = G2::new(builder); +// let neg_sig_g2 = g2.neg(builder, &sig_g2); + +// let (hm0, hm1) = g2.hash_to_fp(builder, self.msg.to_vec()); +// let res = g2.map_to_g2(builder, &hm0, &hm1); + +// let p_array = vec![one_g1, pubkey_g1]; +// let mut q_array = [ +// G2Affine { +// p: neg_sig_g2, +// lines: LineEvaluations::default(), +// }, +// G2Affine { +// p: res, +// lines: LineEvaluations::default(), +// }, +// ]; +// pairing +// .pairing_check(builder, &p_array, &mut q_array) +// .unwrap(); +// pairing.ext12.ext6.ext2.curve_f.check_mul(builder); +// pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); +// pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); +// pairing.ext12.ext6.ext2.curve_f.table.final_check(builder); +// } +// } + +// #[test] +// fn test_pairing_circuit() { + +// /* +// att 0 +// att.Data.Slot 9280000 +// att.Data.CommitteeIndex 0 +// att.Data.BeaconBlockRoot [31 28 22 87 106 251 75 169 100 167 224 201 6 63 144 105 213 235 18 224 169 157 122 56 47 48 28 31 124 69 38 248] +// att.Data.Source 289999 [194 212 152 232 56 145 101 103 73 230 240 242 89 129 63 184 38 157 86 185 251 148 157 68 227 144 241 74 228 200 206 199] +// att.Data.Target 290000 [31 28 22 87 106 251 75 169 100 167 224 201 6 63 144 105 213 235 18 224 169 157 122 56 47 48 28 31 124 69 38 248] +// att.Signature [170 121 191 2 187 22 51 113 109 233 89 181 237 140 207 117 72 230 115 61 124 161 23 145 241 245 211 134 175 182 206 188 124 240 51 154 121 27 217 24 126 83 70 24 90 206 50 148 2 182 65 209 6 215 131 231 254 32 229 193 207 91 52 22 89 10 212 80 4 160 179 150 246 97 120 81 28 231 36 195 223 118 194 250 230 31 182 130 163 236 45 222 26 229 163 89] +// */ diff --git a/efc/src/end2end.rs b/efc/src/end2end.rs new file mode 100644 index 00000000..cbbca721 --- /dev/null +++ b/efc/src/end2end.rs @@ -0,0 +1,40 @@ +use crate::bls_verifier::generate_pairing_witnesses; +use crate::hashtable::generate_hash_witnesses; +use crate::permutation::generate_permutation_hashes_witness; +use crate::shuffle::generate_shuffle_witnesses; +use std::thread; + +pub fn end2end_witness(dir: &str) { + let start_time = std::time::Instant::now(); + let dir_str1 = dir.to_string(); + let shuffle_thread = thread::spawn(move || { + generate_shuffle_witnesses(&dir_str1); + }); + + let dir_str = dir.to_string(); + let hash_thread = thread::spawn(move || { + generate_hash_witnesses(&dir_str); + }); + + let dir_str = dir.to_string(); + let pairing_thread = thread::spawn(move || { + generate_pairing_witnesses(&dir_str); + }); + + let dir_str = dir.to_string(); + let permutation_hash_thread = thread::spawn(move || { + generate_permutation_hashes_witness(&dir_str); + }); + + shuffle_thread.join().expect("Shuffle thread panicked"); + hash_thread.join().expect("Hash thread panicked"); + pairing_thread.join().expect("Pairing thread panicked"); + permutation_hash_thread + .join() + .expect("Permutation hash thread panicked"); + let end_time = std::time::Instant::now(); + println!( + "generate end2end witness, time: {:?}", + end_time.duration_since(start_time) + ); +} diff --git a/efc/src/hashtable.rs b/efc/src/hashtable.rs new file mode 100644 index 00000000..92f82f11 --- /dev/null +++ b/efc/src/hashtable.rs @@ -0,0 +1,153 @@ +use crate::utils::{ensure_directory_exists, read_from_json_file}; +use ark_std::primitive::u8; +use circuit_std_rs::sha256::m31::check_sha256_37bytes; +use circuit_std_rs::sha256::m31_utils::big_array_add; +use circuit_std_rs::utils::register_hint; +use expander_compiler::circuit::ir::hint_normalized::witness_solver; +use expander_compiler::frontend::extra::*; +use expander_compiler::frontend::*; +use serde::Deserialize; +use std::sync::Arc; +use std::thread; + +pub const SHA256LEN: usize = 32; +pub const HASHTABLESIZE: usize = 32; +#[derive(Clone, Copy, Debug)] +pub struct HashTableParams { + pub table_size: usize, + pub hash_len: usize, +} +#[derive(Debug, Deserialize)] +pub struct HashTableJson { + #[serde(rename = "Seed")] + pub seed: Vec, + #[serde(rename = "ShuffleRound")] + pub shuffle_round: u8, + #[serde(rename = "StartIndex")] + pub start_index: Vec, + #[serde(rename = "HashOutputs")] + pub hash_outputs: Vec>, +} +#[derive(Debug, Deserialize)] +pub struct HashTablesJson { + pub tables: Vec, +} + +declare_circuit!(HASHTABLECircuit { + shuffle_round: Variable, + start_index: [Variable; 4], + seed: [PublicVariable; SHA256LEN], + output: [[Variable; SHA256LEN]; HASHTABLESIZE], +}); +impl Define for HASHTABLECircuit { + fn define>(&self, builder: &mut Builder) { + let mut indices = vec![Vec::::new(); HASHTABLESIZE]; + if HASHTABLESIZE > 256 { + panic!("HASHTABLESIZE > 256") + } + let var0 = builder.constant(0); + for (i, cur_index) in indices.iter_mut().enumerate().take(HASHTABLESIZE) { + //assume HASHTABLESIZE is less than 2^8 + let var_i = builder.constant(i as u32); + let index = big_array_add(builder, &self.start_index, &[var_i, var0, var0, var0], 8); + *cur_index = index.to_vec(); + } + for (i, index) in indices.iter().enumerate().take(HASHTABLESIZE) { + let mut cur_input = Vec::::new(); + cur_input.extend_from_slice(&self.seed); + cur_input.push(self.shuffle_round); + cur_input.extend_from_slice(index); + let mut data = cur_input; + data.append(&mut self.output[i].to_vec()); + check_sha256_37bytes(builder, &data); + } + } +} + +pub fn generate_hash_witnesses(dir: &str) { + println!("preparing solver..."); + ensure_directory_exists("./witnesses/hashtable"); + let file_name = "solver_hashtable32.txt"; + let w_s = if std::fs::metadata(file_name).is_ok() { + println!("The solver exists!"); + witness_solver::WitnessSolver::deserialize_from(std::fs::File::open(file_name).unwrap()) + .unwrap() + } else { + println!("The solver does not exist."); + let compile_result = + compile(&HASHTABLECircuit::default(), CompileOptions::default()).unwrap(); + compile_result + .witness_solver + .serialize_into(std::fs::File::create(file_name).unwrap()) + .unwrap(); + let CompileResult { + witness_solver, + layered_circuit, + } = compile_result; + let file = std::fs::File::create("circuit_hashtable32.txt").unwrap(); + let writer = std::io::BufWriter::new(file); + layered_circuit.serialize_into(writer).unwrap(); + witness_solver + }; + let witness_solver = Arc::new(w_s); + + println!("generating witnesses..."); + let start_time = std::time::Instant::now(); + + let file_path = format!("{}/hash_assignment.json", dir); + + let hashtable_data: Vec = read_from_json_file(&file_path).unwrap(); + let mut assignments = vec![]; + for cur_hashtable_data in &hashtable_data { + let mut hash_assignment = HASHTABLECircuit::default(); + for j in 0..32 { + hash_assignment.seed[j] = M31::from(cur_hashtable_data.seed[j] as u32); + } + hash_assignment.shuffle_round = M31::from(cur_hashtable_data.shuffle_round as u32); + for j in 0..4 { + hash_assignment.start_index[j] = M31::from(cur_hashtable_data.start_index[j] as u32); + } + for j in 0..HASHTABLESIZE { + for k in 0..32 { + hash_assignment.output[j][k] = + M31::from(cur_hashtable_data.hash_outputs[j][k] as u32); + } + } + assignments.push(hash_assignment); + } + + let end_time = std::time::Instant::now(); + println!( + "assigned assignments time: {:?}", + end_time.duration_since(start_time) + ); + let assignment_chunks: Vec>> = + assignments.chunks(16).map(|x| x.to_vec()).collect(); + + let handles = assignment_chunks + .into_iter() + .enumerate() + .map(|(i, assignments)| { + let witness_solver = Arc::clone(&witness_solver); + thread::spawn(move || { + let mut hint_registry1 = HintRegistry::::new(); + register_hint(&mut hint_registry1); + let witness = witness_solver + .solve_witnesses_with_hints(&assignments, &mut hint_registry1) + .unwrap(); + let file_name = format!("./witnesses/hashtable/witness_{}.txt", i); + let file = std::fs::File::create(file_name).unwrap(); + let writer = std::io::BufWriter::new(file); + witness.serialize_into(writer).unwrap(); + }) + }) + .collect::>(); + for handle in handles { + handle.join().unwrap(); + } + let end_time = std::time::Instant::now(); + println!( + "Generate hashtable witness Time: {:?}", + end_time.duration_since(start_time) + ); +} diff --git a/efc/src/lib.rs b/efc/src/lib.rs new file mode 100644 index 00000000..65b3cf8d --- /dev/null +++ b/efc/src/lib.rs @@ -0,0 +1,11 @@ +pub mod traits; +pub use traits::StdCircuit; +pub mod attestation; +pub mod bls; +pub mod bls_verifier; +pub mod end2end; +pub mod hashtable; +pub mod permutation; +pub mod shuffle; +pub mod utils; +pub mod validator; diff --git a/efc/src/main.rs b/efc/src/main.rs new file mode 100644 index 00000000..2232667a --- /dev/null +++ b/efc/src/main.rs @@ -0,0 +1,21 @@ +use std::env; + +use efc::end2end::end2end_witness; + +fn main() { + let args: Vec = env::args().collect(); + + // 查找 `-f` 参数的值 + if let Some(f_index) = args.iter().position(|x| x == "-d") { + if let Some(dir) = args.get(f_index + 1) { + println!("The directory of -d is: {}", dir); + end2end_witness(dir); + } else { + println!("Directory is not specified, default dir is the current directory"); + end2end_witness("."); + } + } else { + println!("Directory is not specified, default dir is the current directory"); + end2end_witness("."); + } +} diff --git a/efc/src/permutation.rs b/efc/src/permutation.rs new file mode 100644 index 00000000..b9708a8d --- /dev/null +++ b/efc/src/permutation.rs @@ -0,0 +1,286 @@ +use crate::utils::{ensure_directory_exists, read_from_json_file}; +use circuit_std_rs::logup::LogUpSingleKeyTable; +use circuit_std_rs::poseidon_m31::*; +use circuit_std_rs::sha256::m31_utils::*; +use circuit_std_rs::utils::{register_hint, simple_lookup2, simple_select}; +use expander_compiler::circuit::ir::hint_normalized::witness_solver; +use expander_compiler::frontend::extra::*; +use expander_compiler::frontend::*; +use serde::Deserialize; +use std::sync::Arc; +use std::thread; + +pub const TABLE_SIZE: usize = 1024; +declare_circuit!(PermutationHashCircuit { + index: [Variable; TABLE_SIZE], + value: [Variable; TABLE_SIZE], + table: [Variable; TABLE_SIZE], +}); + +impl Define for PermutationHashCircuit { + fn define>(&self, builder: &mut Builder) { + let mut table = LogUpSingleKeyTable::new(8); + let mut table_key = vec![]; + for i in 0..TABLE_SIZE { + table_key.push(builder.constant(i as u32)); + } + let mut table_values = vec![]; + for i in 0..TABLE_SIZE { + table_values.push(vec![self.table[i]]); + } + table.new_table(table_key, table_values); + let mut query_values = vec![]; + for i in 0..TABLE_SIZE { + query_values.push(vec![self.value[i]]); + } + table.batch_query(self.index.to_vec(), query_values); + //m31 field, repeat 3 times + table.final_check(builder); + table.final_check(builder); + table.final_check(builder); + } +} + +#[test] +fn test_permutation_hash() { + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = PermutationHashCircuit:: { + index: [M31::from(0); TABLE_SIZE], + value: [M31::from(0); TABLE_SIZE], + table: [M31::from(0); TABLE_SIZE], + }; + for i in 0..TABLE_SIZE { + assignment.index[i] = M31::from(i as u32); + assignment.value[i] = M31::from((i as u32 + 571) * 79); + assignment.table[i] = M31::from((i as u32 + 571) * 79); + } + debug_eval( + &PermutationHashCircuit::default(), + &assignment, + hint_registry, + ); +} + +pub const QUERY_SIZE: usize = 1024 * 1024; +pub const VALIDATOR_COUNT: usize = QUERY_SIZE * 2; +declare_circuit!(PermutationIndicesValidatorHashesCircuit { + query_indices: [Variable; QUERY_SIZE], + query_validator_hashes: [[Variable; POSEIDON_M31X16_RATE]; QUERY_SIZE], + active_validator_bits_hash: [Variable; POSEIDON_M31X16_RATE], + active_validator_bits: [Variable; VALIDATOR_COUNT], + table_validator_hashes: [[Variable; POSEIDON_M31X16_RATE]; VALIDATOR_COUNT], + real_keys: [Variable; VALIDATOR_COUNT], +}); +#[derive(Debug, Clone, Deserialize)] +pub struct PermutationHashEntry { + #[serde(rename = "QueryIndices")] + pub query_indices: Vec, + #[serde(rename = "QueryValidatorHashes")] + pub query_validator_hashes: Vec>, + #[serde(rename = "ActiveValidatorBitsHash")] + pub active_validator_bits_hash: Vec, + #[serde(rename = "ActiveValidatorBits")] + pub active_validator_bits: Vec, + #[serde(rename = "TableValidatorHashes")] + pub table_validator_hashes: Vec>, + #[serde(rename = "RealKeys")] + pub real_keys: Vec, +} + +impl Define for PermutationIndicesValidatorHashesCircuit { + fn define>(&self, builder: &mut Builder) { + let zero_var = builder.constant(0); + let neg_one_count = builder.sub(1, VALIDATOR_COUNT as u32); + //check the activeValidatorBitsHash + if self.active_validator_bits.len() % 16 != 0 { + panic!("activeValidatorBits length must be multiple of 16") + } + let mut active_validator_16_bits = vec![]; + for i in 0..VALIDATOR_COUNT / 16 { + active_validator_16_bits.push(from_binary( + builder, + self.active_validator_bits[i * 16..(i + 1) * 16].to_vec(), + )); + } + let params = PoseidonM31Params::new( + builder, + POSEIDON_M31X16_RATE, + 16, + POSEIDON_M31X16_FULL_ROUNDS, + POSEIDON_M31X16_PARTIAL_ROUNDS, + ); + let active_validator_hash = params.hash_to_state(builder, &active_validator_16_bits); + for (i, active_validator_hashbit) in active_validator_hash + .iter() + .enumerate() + .take(POSEIDON_M31X16_RATE) + { + builder.assert_is_equal(active_validator_hashbit, self.active_validator_bits_hash[i]); + } + //move inactive validators to the end + let mut sorted_table_key = [Variable::default(); VALIDATOR_COUNT]; + sorted_table_key[..VALIDATOR_COUNT].copy_from_slice(&self.real_keys[..VALIDATOR_COUNT]); //if active, use curKey, else use curInactiveKey + //for the first one, if active, use 0, else use -ValidatorCount + let shift = simple_select( + builder, + self.active_validator_bits[0], + zero_var, + neg_one_count, + ); + let shift_key = builder.add(sorted_table_key[0], shift); + let shift_key_zero = builder.is_zero(shift_key); + builder.assert_is_equal(shift_key_zero, 1); //the first key must be 0 or ValidatorCount-1 + for i in 1..VALIDATOR_COUNT { + //for every validator, its key can be + //active and active: previous key + 1 + //active and inactive: previous key - ValidatorCount + 1 + //inactive and active: previous key + ValidatorCount + //inactive and inactive: previous key + //1 1 --> previous key + 1 + //1 0 --> previous key - ValidatorCount + 1 + //0 1 --> previous key + ValidatorCount + //0 0 --> previous key + let previous_plus_one = builder.add(sorted_table_key[i - 1], 1); + let previous_minus_count_plus_one = + builder.sub(previous_plus_one, VALIDATOR_COUNT as u32); + let previous_plus_count = builder.add(sorted_table_key[i - 1], VALIDATOR_COUNT as u32); + let expected_key = simple_lookup2( + builder, + self.active_validator_bits[i - 1], + self.active_validator_bits[i], + sorted_table_key[i - 1], + previous_plus_count, + previous_minus_count_plus_one, + previous_plus_one, + ); + //if current one is active, the diff must be 1. Otherwise, the diff must be 0. That is, always equal to activeValidatorBits[i] + let diff = builder.sub(expected_key, sorted_table_key[i]); + let diff_zero = builder.is_zero(diff); + builder.assert_is_equal(diff_zero, 1); + } + //logup + let mut logup = LogUpSingleKeyTable::new(8); + let mut table_values = vec![]; + for i in 0..VALIDATOR_COUNT { + table_values.push(self.table_validator_hashes[i].to_vec()); + } + //build a table with sorted key, i.e., the inactive validators have been moved to the end + logup.new_table(sorted_table_key.to_vec(), table_values); + //logup + let mut query_values = vec![]; + for i in 0..QUERY_SIZE { + query_values.push(self.query_validator_hashes[i].to_vec()); + } + logup.batch_query(self.query_indices.to_vec(), query_values); + logup.final_check(builder); + logup.final_check(builder); + logup.final_check(builder); + } +} + +pub fn generate_permutation_hashes_witness(dir: &str) { + stacker::grow(32 * 1024 * 1024 * 1024, || { + println!("preparing solver..."); + ensure_directory_exists("./witnesses/permutationhashes"); + let file_name = format!("permutationhashes_{}.witness", VALIDATOR_COUNT); + let w_s = if std::fs::metadata(&file_name).is_ok() { + println!("The solver exists!"); + witness_solver::WitnessSolver::deserialize_from( + std::fs::File::open(&file_name).unwrap(), + ) + .unwrap() + } else { + println!("The solver does not exist."); + let compile_result = compile( + &PermutationIndicesValidatorHashesCircuit::default(), + CompileOptions::default(), + ) + .unwrap(); + compile_result + .witness_solver + .serialize_into(std::fs::File::create(&file_name).unwrap()) + .unwrap(); + compile_result.witness_solver + }; + + let witness_solver = Arc::new(w_s); + + println!("Start generating permutationhash witnesses..."); + let start_time = std::time::Instant::now(); + let file_path = format!("{}/permutationhash_assignment.json", dir); + + let permutation_hash_data: Vec = + read_from_json_file(&file_path).unwrap(); + let permutation_hash_data = &permutation_hash_data[0]; + let end_time = std::time::Instant::now(); + println!( + "loaded permutationhash data time: {:?}", + end_time.duration_since(start_time) + ); + + let mut hint_registry = HintRegistry::::new(); + register_hint(&mut hint_registry); + let mut assignment = PermutationIndicesValidatorHashesCircuit:: { + query_indices: [M31::from(0); QUERY_SIZE], + query_validator_hashes: [[M31::from(0); POSEIDON_M31X16_RATE]; QUERY_SIZE], + active_validator_bits_hash: [M31::from(0); POSEIDON_M31X16_RATE], + active_validator_bits: [M31::from(0); VALIDATOR_COUNT], + table_validator_hashes: [[M31::from(0); POSEIDON_M31X16_RATE]; VALIDATOR_COUNT], + real_keys: [M31::from(0); VALIDATOR_COUNT], + }; + for i in 0..VALIDATOR_COUNT { + for j in 0..POSEIDON_M31X16_RATE { + assignment.table_validator_hashes[i][j] = + M31::from(permutation_hash_data.table_validator_hashes[i][j]); + } + assignment.real_keys[i] = M31::from(permutation_hash_data.real_keys[i]); + assignment.active_validator_bits[i] = + M31::from(permutation_hash_data.active_validator_bits[i]); + } + for i in 0..QUERY_SIZE { + assignment.query_indices[i] = M31::from(permutation_hash_data.query_indices[i]); + for j in 0..POSEIDON_M31X16_RATE { + assignment.query_validator_hashes[i][j] = + M31::from(permutation_hash_data.query_validator_hashes[i][j]); + } + } + for i in 0..POSEIDON_M31X16_RATE { + assignment.active_validator_bits_hash[i] = + M31::from(permutation_hash_data.active_validator_bits_hash[i]); + } + let mut assignments = vec![]; + for _i in 0..16 { + assignments.push(assignment.clone()); + } + let assignment_chunks: Vec>> = + assignments.chunks(16).map(|x| x.to_vec()).collect(); + + let handles = assignment_chunks + .into_iter() + .enumerate() + .map(|(i, assignments)| { + let witness_solver = Arc::clone(&witness_solver); + thread::spawn(move || { + let mut hint_registry1 = HintRegistry::::new(); + register_hint(&mut hint_registry1); + let witness = witness_solver + .solve_witness_with_hints(&assignments[0], &mut hint_registry1) + .unwrap(); + let file_name = format!("./witnesses/permutationhashes/witness_{}.txt", i); + let file = std::fs::File::create(file_name).unwrap(); + let writer = std::io::BufWriter::new(file); + witness.serialize_into(writer).unwrap(); + }) + }) + .collect::>(); + for handle in handles { + handle.join().unwrap(); + } + let end_time = std::time::Instant::now(); + println!( + "Generate permutationhash witness Time: {:?}", + end_time.duration_since(start_time) + ); + }); +} diff --git a/efc/src/shuffle.rs b/efc/src/shuffle.rs new file mode 100644 index 00000000..afe97cc0 --- /dev/null +++ b/efc/src/shuffle.rs @@ -0,0 +1,843 @@ +use crate::attestation::{Attestation, AttestationDataSSZ}; +use crate::bls::check_pubkey_key_bls; +use crate::bls_verifier::{convert_point, G1Json, PairingEntry}; +use crate::utils::{ensure_directory_exists, read_from_json_file}; +use crate::validator::{read_validators, ValidatorPlain, ValidatorSSZ}; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; +use circuit_std_rs::gnark::emulated::sw_bls12381::g1::*; +use circuit_std_rs::gnark::emulated::sw_bls12381::g2::{G2AffP, G2}; +use circuit_std_rs::sha256::m31_utils::big_array_add; +use circuit_std_rs::utils::{register_hint, simple_select}; +use expander_compiler::circuit::ir::hint_normalized::witness_solver; +use expander_compiler::frontend::extra::*; +use expander_compiler::frontend::*; +use serde::de::{Deserializer, SeqAccess, Visitor}; +use serde::Deserialize; +use std::fmt; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +pub const SHUFFLE_ROUND: usize = 90; +pub const VALIDATOR_CHUNK_SIZE: usize = 128 * 4; +pub const MAX_VALIDATOR_EXP: usize = 29; +pub const POSEIDON_HASH_LENGTH: usize = 8; + +#[derive(Debug, Deserialize, Clone)] +pub struct ShuffleJson { + #[serde(rename = "StartIndex")] + pub start_index: u32, + #[serde(rename = "ChunkLength")] + pub chunk_length: u32, + #[serde(rename = "ShuffleIndices", deserialize_with = "deserialize_1d_u32_m31")] + pub shuffle_indices: Vec, + #[serde( + rename = "CommitteeIndices", + deserialize_with = "deserialize_1d_u32_m31" + )] + pub committee_indices: Vec, + #[serde(rename = "Pivots", deserialize_with = "deserialize_1d_u32_m31")] + pub pivots: Vec, + #[serde(rename = "IndexCount")] + pub index_count: u32, + #[serde( + rename = "PositionResults", + deserialize_with = "deserialize_1d_u32_m31" + )] + pub position_results: Vec, + #[serde( + rename = "PositionBitResults", + deserialize_with = "deserialize_1d_u32_m31" + )] + pub position_bit_results: Vec, + #[serde(rename = "FlipResults", deserialize_with = "deserialize_1d_u32_m31")] + pub flip_results: Vec, + #[serde(rename = "Slot")] + pub slot: u32, + #[serde( + rename = "ValidatorHashes", + deserialize_with = "deserialize_2d_u32_m31" + )] + pub validator_hashes: Vec>, + #[serde( + rename = "AggregationBits", + deserialize_with = "deserialize_1d_u32_m31" + )] + pub aggregation_bits: Vec, + #[serde(rename = "AggregatedPubkey")] + pub aggregated_pubkey: G1Json, + #[serde(rename = "AttestationBalance")] + pub attestation_balance: Vec, +} +fn process_i64_value(value: i64) -> u32 { + if value == -1 { + (1u32 << 31) - 2 // p - 1 + } else if value >= 0 { + value as u32 + } else { + panic!("Unexpected negative value other than -1"); + } +} +fn deserialize_1d_u32_m31<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let bits: Vec = Deserialize::deserialize(deserializer)?; + Ok(bits.into_iter().map(process_i64_value).collect()) +} + +fn deserialize_2d_u32_m31<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + struct ValidatorHashesVisitor; + + impl<'de> Visitor<'de> for ValidatorHashesVisitor { + type Value = Vec>; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a nested array of integers") + } + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut outer = Vec::new(); + while let Some(inner) = seq.next_element::>()? { + let processed_inner = inner.into_iter().map(process_i64_value).collect(); + outer.push(processed_inner); + } + Ok(outer) + } + } + + deserializer.deserialize_seq(ValidatorHashesVisitor) +} + +// Define defines the circuit +declare_circuit!(ShuffleCircuit { + start_index: Variable, + chunk_length: Variable, + shuffle_indices: [Variable; VALIDATOR_CHUNK_SIZE], + committee_indices: [Variable; VALIDATOR_CHUNK_SIZE], + pivots: [Variable; SHUFFLE_ROUND], + index_count: Variable, + position_results: [Variable; SHUFFLE_ROUND * VALIDATOR_CHUNK_SIZE], + position_bit_results: [Variable; SHUFFLE_ROUND * VALIDATOR_CHUNK_SIZE], + flip_results: [Variable; SHUFFLE_ROUND * VALIDATOR_CHUNK_SIZE], + //attestationdata + slot: [Variable; 8], + committee_index: [Variable; 8], + beacon_beacon_block_root: [Variable; 32], + source_epoch: [Variable; 8], + target_epoch: [Variable; 8], + source_root: [Variable; 32], + target_root: [Variable; 32], + //attestationhm = hashtog2(attestationdata.signingroot()), a g2 point + attestation_hm: [[[Variable; 48]; 2]; 2], //public hm + //attestationsig + attestation_sig_bytes: [Variable; 96], + attestation_sig_g2: [[[Variable; 48]; 2]; 2], //public sig, unmarsalled from attestation_sig_bytes + aggregation_bits: [Variable; VALIDATOR_CHUNK_SIZE], + validator_hashes: [[Variable; POSEIDON_HASH_LENGTH]; VALIDATOR_CHUNK_SIZE], + aggregated_pubkey: [[Variable; 48]; 2], //public public_key + attestation_balance: [Variable; 8], + pubkeys_bls: [[[Variable; 48]; 2]; VALIDATOR_CHUNK_SIZE], + // validators: [ValidatorSSZ;VALIDATOR_CHUNK_SIZE], + pubkey: [[Variable; 48]; VALIDATOR_CHUNK_SIZE], + withdrawal_credentials: [[Variable; 32]; VALIDATOR_CHUNK_SIZE], + effective_balance: [[Variable; 8]; VALIDATOR_CHUNK_SIZE], + slashed: [[Variable; 1]; VALIDATOR_CHUNK_SIZE], + activation_eligibility_epoch: [[Variable; 8]; VALIDATOR_CHUNK_SIZE], + activation_epoch: [[Variable; 8]; VALIDATOR_CHUNK_SIZE], + exit_epoch: [[Variable; 8]; VALIDATOR_CHUNK_SIZE], + withdrawable_epoch: [[Variable; 8]; VALIDATOR_CHUNK_SIZE], +}); + +impl ShuffleCircuit { + pub fn from_plains( + &mut self, + shuffle_json: &ShuffleJson, + plain_validators: &[ValidatorPlain], + pubkey_bls: &[Vec], + attestation: &Attestation, + pairing_entry: &PairingEntry, + ) { + if shuffle_json.committee_indices.len() != VALIDATOR_CHUNK_SIZE { + panic!("committee_indices length is not equal to VALIDATOR_CHUNK_SIZE"); + } + //assign shuffle_json + self.start_index = M31::from(shuffle_json.start_index); + self.chunk_length = M31::from(shuffle_json.chunk_length); + for i in 0..VALIDATOR_CHUNK_SIZE { + self.shuffle_indices[i] = M31::from(shuffle_json.shuffle_indices[i]); + self.committee_indices[i] = M31::from(shuffle_json.committee_indices[i]); + self.aggregation_bits[i] = M31::from(shuffle_json.aggregation_bits[i]); + } + for i in 0..SHUFFLE_ROUND { + self.pivots[i] = M31::from(shuffle_json.pivots[i]); + } + self.index_count = M31::from(shuffle_json.index_count); + for i in 0..SHUFFLE_ROUND * VALIDATOR_CHUNK_SIZE { + self.position_results[i] = M31::from(shuffle_json.position_results[i]); + self.position_bit_results[i] = M31::from(shuffle_json.position_bit_results[i]); + self.flip_results[i] = M31::from(shuffle_json.flip_results[i]); + } + + //assign validator_hashes + for i in 0..VALIDATOR_CHUNK_SIZE { + for j in 0..POSEIDON_HASH_LENGTH { + self.validator_hashes[i][j] = M31::from(shuffle_json.validator_hashes[i][j]); + } + } + + //assign aggregated_pubkey + let pubkey = &shuffle_json.aggregated_pubkey; + for i in 0..48 { + self.aggregated_pubkey[0][i] = M31::from(pubkey.x.limbs[i] as u32); + self.aggregated_pubkey[1][i] = M31::from(pubkey.y.limbs[i] as u32); + } + + //assign attestation_balance + for i in 0..8 { + self.attestation_balance[i] = M31::from(shuffle_json.attestation_balance[i]); + } + + for i in 0..VALIDATOR_CHUNK_SIZE { + //assign pubkey_bls + let raw_pubkey_bls = &pubkey_bls[shuffle_json.committee_indices[i] as usize]; + let pubkey_bls_x = STANDARD.decode(&raw_pubkey_bls[0]).unwrap(); + let pubkey_bls_y = STANDARD.decode(&raw_pubkey_bls[1]).unwrap(); + for k in 0..48 { + self.pubkeys_bls[i][0][k] = M31::from(pubkey_bls_x[47 - k] as u32); + self.pubkeys_bls[i][1][k] = M31::from(pubkey_bls_y[47 - k] as u32); + } + + //assign validator + let validator = plain_validators[shuffle_json.committee_indices[i] as usize].clone(); + + //assign pubkey + let raw_pubkey = validator.public_key.clone(); + let pubkey = STANDARD.decode(raw_pubkey).unwrap(); + for (j, pubkey_byte) in pubkey.iter().enumerate().take(48) { + self.pubkey[i][j] = M31::from(*pubkey_byte as u32); + } + //assign withdrawal_credentials + let raw_withdrawal_credentials = validator.withdrawal_credentials.clone(); + let withdrawal_credentials = STANDARD.decode(raw_withdrawal_credentials).unwrap(); + for (j, withdrawal_credentials_byte) in + withdrawal_credentials.iter().enumerate().take(32) + { + self.withdrawal_credentials[i][j] = M31::from(*withdrawal_credentials_byte as u32); + } + //assign effective_balance + let effective_balance = validator.effective_balance.to_le_bytes(); + for (j, effective_balance_byte) in effective_balance.iter().enumerate() { + self.effective_balance[i][j] = M31::from(*effective_balance_byte as u32); + } + //assign slashed + let slashed = if validator.slashed { 1 } else { 0 }; + self.slashed[i][0] = M31::from(slashed); + //assign activation_eligibility_epoch + let activation_eligibility_epoch = validator.activation_eligibility_epoch.to_le_bytes(); + for (j, activation_eligibility_epoch_byte) in + activation_eligibility_epoch.iter().enumerate() + { + self.activation_eligibility_epoch[i][j] = + M31::from(*activation_eligibility_epoch_byte as u32); + } + //assign activation_epoch + let activation_epoch = validator.activation_epoch.to_le_bytes(); + for (j, activation_epoch_byte) in activation_epoch.iter().enumerate() { + self.activation_epoch[i][j] = M31::from(*activation_epoch_byte as u32); + } + //assign exit_epoch + let exit_epoch = validator.exit_epoch.to_le_bytes(); + for (j, exit_epoch_byte) in exit_epoch.iter().enumerate() { + self.exit_epoch[i][j] = M31::from(*exit_epoch_byte as u32); + } + //assign withdrawable_epoch + let withdrawable_epoch = validator.withdrawable_epoch.to_le_bytes(); + for (j, withdrawable_epoch_byte) in withdrawable_epoch.iter().enumerate() { + self.withdrawable_epoch[i][j] = M31::from(*withdrawable_epoch_byte as u32); + } + + //assign slot + let slot = attestation.data.slot.to_le_bytes(); + for (j, slot_byte) in slot.iter().enumerate() { + self.slot[j] = M31::from(*slot_byte as u32); + } + //assign committee_index + let committee_index = attestation.data.committee_index.to_le_bytes(); + for (j, committee_index_byte) in committee_index.iter().enumerate() { + self.committee_index[j] = M31::from(*committee_index_byte as u32); + } + //assign beacon_beacon_block_root + let beacon_beacon_block_root = attestation.data.beacon_block_root.clone(); + let beacon_beacon_block_root = STANDARD.decode(beacon_beacon_block_root).unwrap(); + for (j, beacon_beacon_block_root_byte) in beacon_beacon_block_root.iter().enumerate() { + self.beacon_beacon_block_root[j] = M31::from(*beacon_beacon_block_root_byte as u32); + } + //assign source_epoch + let source_epoch = attestation.data.source.epoch.to_le_bytes(); + for (j, source_epoch_byte) in source_epoch.iter().enumerate() { + self.source_epoch[j] = M31::from(*source_epoch_byte as u32); + } + //assign target_epoch + let target_epoch = attestation.data.target.epoch.to_le_bytes(); + for (j, target_epoch_byte) in target_epoch.iter().enumerate() { + self.target_epoch[j] = M31::from(*target_epoch_byte as u32); + } + //assign source_root + let source_root = attestation.data.source.root.clone(); + let source_root = STANDARD.decode(source_root).unwrap(); + for (j, source_root_byte) in source_root.iter().enumerate() { + self.source_root[j] = M31::from(*source_root_byte as u32); + } + //assign target_root + let target_root = attestation.data.target.root.clone(); + let target_root = STANDARD.decode(target_root).unwrap(); + for (j, target_root_byte) in target_root.iter().enumerate() { + self.target_root[j] = M31::from(*target_root_byte as u32); + } + //assign attestation_hm + self.attestation_hm[0] = convert_point(pairing_entry.hm.p.x.clone()); + self.attestation_hm[1] = convert_point(pairing_entry.hm.p.y.clone()); + + //assign attestation_sig_bytes + let attestation_sig_bytes = attestation.signature.clone(); + let attestation_sig_bytes = STANDARD.decode(attestation_sig_bytes).unwrap(); + for (j, attestation_sig_byte) in attestation_sig_bytes.iter().enumerate() { + self.attestation_sig_bytes[j] = M31::from(*attestation_sig_byte as u32); + } + //assign attestation_sig_g2 + self.attestation_sig_g2[0] = convert_point(pairing_entry.signature.p.x.clone()); + self.attestation_sig_g2[1] = convert_point(pairing_entry.signature.p.y.clone()); + } + } + pub fn from_pubkey_bls(&mut self, committee_indices: Vec, pubkey_bls: Vec>) { + for i in 0..VALIDATOR_CHUNK_SIZE { + let pubkey = &pubkey_bls[committee_indices[i] as usize]; + let pubkey_x = STANDARD.decode(&pubkey[0]).unwrap(); + let pubkey_y = STANDARD.decode(&pubkey[1]).unwrap(); + for k in 0..48 { + self.pubkeys_bls[i][0][k] = M31::from(pubkey_x[k] as u32); + self.pubkeys_bls[i][1][k] = M31::from(pubkey_y[k] as u32); + } + } + } +} +impl Define for ShuffleCircuit { + fn define>(&self, builder: &mut Builder) { + let mut g1 = G1::new(builder); + + let mut indices_chunk = get_indice_chunk( + builder, + self.start_index, + self.chunk_length, + VALIDATOR_CHUNK_SIZE, + ); + + //set padding indices to 0 + let zero_var = builder.constant(0); + for (i, chunk) in indices_chunk.iter_mut().enumerate() { + let tmp = builder.add(self.flip_results[i], 1); + let ignore_flag = builder.is_zero(tmp); + *chunk = simple_select(builder, ignore_flag, zero_var, *chunk); + } + //flip the indices based on the hashbit + let mut copy_cur_indices = indices_chunk.clone(); + for i in 0..SHUFFLE_ROUND { + let (cur_indices, diffs) = flip_with_hash_bits( + builder, + self.pivots[i], + self.index_count, + ©_cur_indices, + &self.position_results[i * VALIDATOR_CHUNK_SIZE..(i + 1) * VALIDATOR_CHUNK_SIZE], + &self.position_bit_results + [i * VALIDATOR_CHUNK_SIZE..(i + 1) * VALIDATOR_CHUNK_SIZE], + &self.flip_results[i * VALIDATOR_CHUNK_SIZE..(i + 1) * VALIDATOR_CHUNK_SIZE], + ); + for diff in diffs { + g1.curve_f + .table + .rangeproof(builder, diff, MAX_VALIDATOR_EXP); + } + copy_cur_indices = + builder.new_hint("myhint.copyvarshint", &cur_indices, cur_indices.len()); + } + //check the final curIndices, should be equal to the shuffleIndex + for (i, cur_index) in copy_cur_indices + .iter_mut() + .enumerate() + .take(self.shuffle_indices.len()) + { + let tmp = builder.add(self.flip_results[i], 1); + let is_minus_one = builder.is_zero(tmp); + *cur_index = simple_select(builder, is_minus_one, self.shuffle_indices[i], *cur_index); + let tmp = builder.sub(self.shuffle_indices[i], *cur_index); + let tmp_res = builder.is_zero(tmp); + builder.assert_is_equal(tmp_res, 1); + } + + let mut pubkey_list = vec![]; + let mut acc_balance = vec![]; + for i in 0..VALIDATOR_CHUNK_SIZE { + pubkey_list.push(self.pubkey[i]); + acc_balance.push(self.effective_balance[i]); + } + let effect_balance = calculate_balance(builder, &mut acc_balance, &self.aggregation_bits); + for (i, cur_effect_balance) in effect_balance.iter().enumerate() { + builder.assert_is_equal(cur_effect_balance, self.attestation_balance[i]); + } + + let mut pubkey_list_bls = vec![]; + for (i, cur_pubkey) in pubkey_list.iter().enumerate() { + let pubkey_g1 = G1Affine::from_vars( + self.pubkeys_bls[i][0].to_vec(), + self.pubkeys_bls[i][1].to_vec(), + ); + let logup_var = check_pubkey_key_bls(builder, cur_pubkey.to_vec(), &pubkey_g1); + g1.curve_f.table.rangeproof(builder, logup_var, 5); + pubkey_list_bls.push(pubkey_g1); + } + + let mut aggregated_pubkey = G1Affine::from_vars( + self.aggregated_pubkey[0].to_vec(), + self.aggregated_pubkey[1].to_vec(), + ); + aggregate_attestation_public_key( + builder, + &mut g1, + &pubkey_list_bls, + &self.aggregation_bits, + &mut aggregated_pubkey, + ); + + for index in 0..VALIDATOR_CHUNK_SIZE { + let mut validator = ValidatorSSZ::new(); + for i in 0..48 { + validator.public_key[i] = self.pubkey[index][i]; + } + for i in 0..32 { + validator.withdrawal_credentials[i] = self.withdrawal_credentials[index][i]; + } + for i in 0..8 { + validator.effective_balance[i] = self.effective_balance[index][i]; + } + for i in 0..1 { + validator.slashed[i] = self.slashed[index][i]; + } + for i in 0..8 { + validator.activation_eligibility_epoch[i] = + self.activation_eligibility_epoch[index][i]; + } + for i in 0..8 { + validator.activation_epoch[i] = self.activation_epoch[index][i]; + } + for i in 0..8 { + validator.exit_epoch[i] = self.exit_epoch[index][i]; + } + for i in 0..8 { + validator.withdrawable_epoch[i] = self.withdrawable_epoch[index][i]; + } + let hash = validator.hash(builder); + for (i, hashbit) in hash.iter().enumerate().take(8) { + builder.assert_is_equal(hashbit, self.validator_hashes[index][i]); + } + } + // attestation + let att_ssz = AttestationDataSSZ { + slot: self.slot, + committee_index: self.committee_index, + beacon_block_root: self.beacon_beacon_block_root, + source_epoch: self.source_epoch, + target_epoch: self.target_epoch, + source_root: self.source_root, + target_root: self.target_root, + }; + let mut g2 = G2::new(builder); + // domain + let domain = [ + 1, 0, 0, 0, 187, 164, 218, 150, 53, 76, 159, 37, 71, 108, 241, 188, 105, 191, 88, 58, + 127, 158, 10, 240, 73, 48, 91, 98, 222, 103, 102, 64, + ]; + let mut domain_var = vec![]; + for domain_byte in domain.iter() { + domain_var.push(builder.constant(*domain_byte as u32)); + } + let att_hash = att_ssz.att_data_signing_root(builder, &domain_var); //msg + //map to hm + let (hm0, hm1) = g2.hash_to_fp(builder, &att_hash); + let hm_g2 = g2.map_to_g2(builder, &hm0, &hm1); + let expected_hm_g2 = G2AffP::from_vars( + self.attestation_hm[0][0].to_vec(), + self.attestation_hm[0][1].to_vec(), + self.attestation_hm[1][0].to_vec(), + self.attestation_hm[1][1].to_vec(), + ); + g2.assert_is_equal(builder, &hm_g2, &expected_hm_g2); + // unmarshal attestation sig + let sig_g2 = g2.uncompressed(builder, &self.attestation_sig_bytes); + let expected_sig_g2 = G2AffP::from_vars( + self.attestation_sig_g2[0][0].to_vec(), + self.attestation_sig_g2[0][1].to_vec(), + self.attestation_sig_g2[1][0].to_vec(), + self.attestation_sig_g2[1][1].to_vec(), + ); + g2.assert_is_equal(builder, &sig_g2, &expected_sig_g2); + g2.ext2.curve_f.check_mul(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + g2.ext2.curve_f.table.final_check(builder); + + g1.curve_f.check_mul(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + g1.curve_f.table.final_check(builder); + } +} + +pub fn get_indice_chunk>( + builder: &mut B, + start: Variable, + length: Variable, + max_len: usize, +) -> Vec { + let mut res = vec![]; + //M31_MOD = 2147483647 + let neg_one = builder.constant(2147483647 - 1); + for i in 0..max_len { + let tmp = builder.sub(length, i as u32); + let reach_end = builder.is_zero(tmp); + let mut tmp = builder.add(start, i as u32); + tmp = simple_select(builder, reach_end, neg_one, tmp); + res.push(tmp); + } + res +} +pub fn calculate_balance>( + builder: &mut B, + acc_balance: &mut [[Variable; 8]], + aggregation_bits: &[Variable], +) -> Vec { + if acc_balance.is_empty() || acc_balance[0].is_empty() { + panic!("accBalance is empty or invalid balance"); + } else if acc_balance.len() == 1 { + return acc_balance[0].to_vec(); + } + //initialize the balance + let mut cur_balance = vec![builder.constant(0); acc_balance[0].len()]; + let zero_var = builder.constant(0); + + //set the balance to 0 if aggregationBits[i] = 0 + for i in 0..aggregation_bits.len() { + for j in 0..acc_balance[i].len() { + acc_balance[i][j] = + simple_select(builder, aggregation_bits[i], acc_balance[i][j], zero_var); + } + } + //since balance is [8]frontend.Variable, we need to support Array addition + for balance in acc_balance { + cur_balance = big_array_add(builder, &cur_balance, balance, cur_balance.len()); + } + cur_balance +} +pub fn flip_with_hash_bits>( + builder: &mut B, + pivot: Variable, + index_count: Variable, + cur_indices: &[Variable], + position_results: &[Variable], + position_bit_results: &[Variable], + flip_results: &[Variable], +) -> (Vec, Vec) { + let mut res = vec![]; + let mut position_diffs = vec![]; + for i in 0..cur_indices.len() { + let tmp = builder.add(flip_results[i], 1); + let ignore_flag = builder.is_zero(tmp); + let tmp = builder.sub(pivot, cur_indices[i]); + let tmp = builder.sub(tmp, flip_results[i]); + let flip_flag1 = builder.is_zero(tmp); + let tmp = builder.add(index_count, pivot); + let tmp = builder.sub(tmp, cur_indices[i]); + let tmp = builder.sub(tmp, flip_results[i]); + let flip_flag2 = builder.is_zero(tmp); + let tmp = builder.or(flip_flag1, flip_flag2); + let flip_flag = builder.or(tmp, ignore_flag); + builder.assert_is_equal(flip_flag, 1); + + let tmp = builder.sub(position_results[i], flip_results[i]); + let position_flag1 = builder.is_zero(tmp); + let tmp = builder.sub(position_results[i], cur_indices[i]); + let position_flag2 = builder.is_zero(tmp); + let tmp = builder.or(position_flag1, position_flag2); + let position_flag = builder.or(tmp, ignore_flag); + builder.assert_is_equal(position_flag, 1); + + let tmp = builder.mul(2, position_results[i]); + let tmp = builder.sub(tmp, flip_results[i]); + let position_diff = builder.sub(tmp, cur_indices[i]); + let zero_var = builder.constant(0); + let position_diff = simple_select(builder, ignore_flag, zero_var, position_diff); + position_diffs.push(position_diff); + res.push(simple_select( + builder, + position_bit_results[i], + flip_results[i], + cur_indices[i], + )); + } + (res, position_diffs) +} + +pub fn aggregate_attestation_public_key>( + builder: &mut B, + g1: &mut G1, + pub_key: &[G1Affine], + validator_agg_bits: &[Variable], + agg_pubkey: &mut G1Affine, +) { + let one_var = builder.constant(1); + let mut has_first_flag = builder.constant(0); + let mut copy_aggregated_pubkey = pub_key[0].clone(); + has_first_flag = simple_select(builder, validator_agg_bits[0], one_var, has_first_flag); + let mut copy_has_first_flag = builder.new_hint("myhint.copyvarshint", &[has_first_flag], 1)[0]; + for i in 1..validator_agg_bits.len() { + let mut aggregated_pubkey = pub_key[0].clone(); + let tmp_agg_pubkey = g1.add(builder, ©_aggregated_pubkey, &pub_key[i]); + aggregated_pubkey.x = g1.curve_f.select( + builder, + validator_agg_bits[i], + &tmp_agg_pubkey.x, + ©_aggregated_pubkey.x, + ); + aggregated_pubkey.y = g1.curve_f.select( + builder, + validator_agg_bits[i], + &tmp_agg_pubkey.y, + ©_aggregated_pubkey.y, + ); + let no_first_flag = builder.sub(1, copy_has_first_flag); + let is_first = builder.and(validator_agg_bits[i], no_first_flag); + aggregated_pubkey.x = + g1.curve_f + .select(builder, is_first, &pub_key[i].x, &aggregated_pubkey.x); + aggregated_pubkey.y = + g1.curve_f + .select(builder, is_first, &pub_key[i].y, &aggregated_pubkey.y); + has_first_flag = + simple_select(builder, validator_agg_bits[i], one_var, copy_has_first_flag); + copy_aggregated_pubkey = g1.copy_g1(builder, &aggregated_pubkey); + copy_has_first_flag = builder.new_hint("myhint.copyvarshint", &[has_first_flag], 1)[0]; + } + g1.curve_f + .assert_is_equal(builder, ©_aggregated_pubkey.x, &agg_pubkey.x); + g1.curve_f + .assert_is_equal(builder, ©_aggregated_pubkey.y, &agg_pubkey.y); +} + +pub fn aggregate_attestation_public_key2>( + builder: &mut B, + g1: &mut G1, + pub_key: &[G1Affine], + validator_agg_bits: &[Variable], + agg_pubkey: &mut G1Affine, +) { + let one_var = builder.constant(1); + let mut has_first_flag = builder.constant(0); + let mut aggregated_pubkey = pub_key[0].clone(); + has_first_flag = simple_select(builder, validator_agg_bits[0], one_var, has_first_flag); + for i in 1..validator_agg_bits.len() { + let tmp_agg_pubkey = g1.add(builder, &aggregated_pubkey, &pub_key[i]); + aggregated_pubkey.x = g1.curve_f.select( + builder, + validator_agg_bits[i], + &tmp_agg_pubkey.x, + &aggregated_pubkey.x, + ); + aggregated_pubkey.y = g1.curve_f.select( + builder, + validator_agg_bits[i], + &tmp_agg_pubkey.y, + &aggregated_pubkey.y, + ); + let no_first_flag = builder.sub(1, has_first_flag); + let is_first = builder.and(validator_agg_bits[i], no_first_flag); + aggregated_pubkey.x = + g1.curve_f + .select(builder, is_first, &pub_key[i].x, &aggregated_pubkey.x); + aggregated_pubkey.y = + g1.curve_f + .select(builder, is_first, &pub_key[i].y, &aggregated_pubkey.y); + has_first_flag = simple_select(builder, validator_agg_bits[i], one_var, has_first_flag); + } + g1.curve_f + .assert_is_equal(builder, &aggregated_pubkey.x, &agg_pubkey.x); + g1.curve_f + .assert_is_equal(builder, &aggregated_pubkey.y, &agg_pubkey.y); +} +pub fn generate_shuffle_witnesses(dir: &str) { + stacker::grow(32 * 1024 * 1024 * 1024, || { + println!("preparing solver..."); + ensure_directory_exists("./witnesses/shuffle"); + + let file_name = "solver_shuffle.txt"; + let w_s = if std::fs::metadata(file_name).is_ok() { + println!("The solver exists!"); + witness_solver::WitnessSolver::deserialize_from(std::fs::File::open(file_name).unwrap()) + .unwrap() + } else { + println!("The solver does not exist."); + let compile_result = + compile(&ShuffleCircuit::default(), CompileOptions::default()).unwrap(); + compile_result + .witness_solver + .serialize_into(std::fs::File::create(file_name).unwrap()) + .unwrap(); + let CompileResult { + witness_solver, + layered_circuit, + } = compile_result; + let file = std::fs::File::create("circuit_shuffle.txt").unwrap(); + let writer = std::io::BufWriter::new(file); + layered_circuit.serialize_into(writer).unwrap(); + witness_solver + }; + let witness_solver = Arc::new(w_s); + + println!("generating witnesses..."); + let start_time = std::time::Instant::now(); + let plain_validators = read_validators(dir); + let file_path = format!("{}/shuffle_assignment.json", dir); + let shuffle_data: Vec = read_from_json_file(&file_path).unwrap(); + let file_path = format!("{}/pubkeyBLSList.json", dir); + let public_key_bls_list: Vec> = read_from_json_file(&file_path).unwrap(); + let file_path = format!("{}/slotAttestationsFolded.json", dir); + let attestations: Vec = read_from_json_file(&file_path).unwrap(); + let file_path = format!("{}/pairing_assignment.json", dir); + let pairing_data: Vec = read_from_json_file(&file_path).unwrap(); + let end_time = std::time::Instant::now(); + println!( + "loaed assignment data, time: {:?}", + end_time.duration_since(start_time) + ); + + let mut handles = vec![]; + let plain_validators = Arc::new(plain_validators); + let public_key_bls_list = Arc::new(public_key_bls_list); + let attestations = Arc::new(attestations); + let assignments = Arc::new(Mutex::new(vec![None; shuffle_data.len() / 2])); + let pairing_data = Arc::new(pairing_data); + + for (i, shuffle_item) in shuffle_data.into_iter().enumerate().take(1024) { + let assignments = Arc::clone(&assignments); + let target_plain_validators = Arc::clone(&plain_validators); + let target_public_key_bls_list = Arc::clone(&public_key_bls_list); + let target_attestations = Arc::clone(&attestations); + let pairing_data = Arc::clone(&pairing_data); + + let handle = thread::spawn(move || { + let mut assignment = ShuffleCircuit::::default(); + assignment.from_plains( + &shuffle_item, + &target_plain_validators, + &target_public_key_bls_list, + &target_attestations[i], + &pairing_data[i], + ); + + let mut assignments = assignments.lock().unwrap(); + assignments[i] = Some(assignment); + }); + + handles.push(handle); + } + + for handle in handles { + handle.join().expect("Thread panicked"); + } + + let end_time = std::time::Instant::now(); + println!( + "assigned assignment data, time: {:?}", + end_time.duration_since(start_time) + ); + + let assignments = assignments + .lock() + .unwrap() + .iter() + .map(|x| x.clone().unwrap()) + .collect::>(); + let assignment_chunks: Vec>> = + assignments.chunks(16).map(|x| x.to_vec()).collect(); + + let handles = assignment_chunks + .into_iter() + .enumerate() + .map(|(i, assignments)| { + let witness_solver = Arc::clone(&witness_solver); + thread::spawn(move || { + let mut hint_registry1 = HintRegistry::::new(); + register_hint(&mut hint_registry1); + let witness = witness_solver + .solve_witnesses_with_hints(&assignments, &mut hint_registry1) + .unwrap(); + let file_name = format!("./witnesses/shuffle/witness_{}.txt", i); + let file = std::fs::File::create(file_name).unwrap(); + let writer = std::io::BufWriter::new(file); + witness.serialize_into(writer).unwrap(); + }) + }) + .collect::>(); + for handle in handles { + handle.join().unwrap(); + } + let end_time = std::time::Instant::now(); + println!( + "Generate shuffle witness Time: {:?}", + end_time.duration_since(start_time) + ); + }); +} + +// #[test] +// fn test_generate_shuffle2_witnesses() { +// generate_shuffle_witnesses("./data"); +// } + +// #[test] +// fn run_shuffle2() { +// let dir = "./data"; +// let mut hint_registry = HintRegistry::::new(); +// register_hint(&mut hint_registry); +// let plain_validators = read_validators(dir); +// let file_path = format!("{}/shuffle_assignment.json", dir); +// let shuffle_data: Vec = read_from_json_file(&file_path).unwrap(); +// let file_path = format!("{}/pubkeyBLSList.json", dir); +// let public_key_bls_list: Vec> = read_from_json_file(&file_path).unwrap(); +// let file_path = format!("{}/slotAttestationsFolded.json", dir); +// let attestations: Vec = read_from_json_file(&file_path).unwrap(); +// let file_path = format!("{}/pairing_assignment.json", dir); +// let pairing_data: Vec = read_from_json_file(&file_path).unwrap(); + +// let mut assignment = ShuffleCircuit::::default(); +// assignment.from_plains( +// &shuffle_data[0], +// &plain_validators, +// &public_key_bls_list, +// &attestations[0], +// &pairing_data[0], +// ); +// let file_name = "shuffle.witness"; +// stacker::grow(32 * 1024 * 1024 * 1024, || { +// let compile_result = +// compile(&ShuffleCircuit::default(), CompileOptions::default()).unwrap(); +// compile_result +// .witness_solver +// .serialize_into(std::fs::File::create(file_name).unwrap()) +// .unwrap(); +// debug_eval(&ShuffleCircuit::default(), &assignment, hint_registry); +// }); +// } diff --git a/efc/src/traits.rs b/efc/src/traits.rs new file mode 100644 index 00000000..f42ca176 --- /dev/null +++ b/efc/src/traits.rs @@ -0,0 +1,14 @@ +use std::fmt::Debug; + +use expander_compiler::frontend::{internal::DumpLoadTwoVariables, Config, Define, Variable}; +use rand::RngCore; + +// All std circuits must implement the following trait +pub trait StdCircuit: Clone + Define + DumpLoadTwoVariables { + type Params: Clone + Debug; + type Assignment: Clone + DumpLoadTwoVariables; + + fn new_circuit(params: &Self::Params) -> Self; + + fn new_assignment(params: &Self::Params, rng: impl RngCore) -> Self::Assignment; +} diff --git a/efc/src/utils.rs b/efc/src/utils.rs new file mode 100644 index 00000000..ba213c91 --- /dev/null +++ b/efc/src/utils.rs @@ -0,0 +1,63 @@ +use expander_compiler::{circuit::layered::witness::Witness, frontend::*}; +use serde::de::DeserializeOwned; +use std::{fs, path::Path}; + +pub fn run_circuit(compile_result: &CompileResult, witness: Witness) { + //can be skipped + let output = compile_result.layered_circuit.run(&witness); + for x in output.iter() { + assert!(*x); + } + + // ########## EXPANDER ########## + + //compile + let mut expander_circuit = compile_result + .layered_circuit + .export_to_expander::() + .flatten(); + let config = expander_config::Config::::new( + expander_config::GKRScheme::Vanilla, + mpi_config::MPIConfig::new(), + ); + + let (simd_input, simd_public_input) = witness.to_simd::(); + println!("{} {}", simd_input.len(), simd_public_input.len()); + expander_circuit.layers[0].input_vals = simd_input; + expander_circuit.public_input = simd_public_input.clone(); + + // prove + expander_circuit.evaluate(); + let mut prover = gkr::Prover::new(&config); + prover.prepare_mem(&expander_circuit); + let (claimed_v, proof) = gkr::executor::prove(&mut expander_circuit, &config); + + // verify + assert!(gkr::executor::verify( + &mut expander_circuit, + &config, + &proof, + &claimed_v + )); +} + +pub fn read_from_json_file( + file_path: &str, +) -> Result> { + let json_content = fs::read_to_string(file_path)?; + + let data: T = serde_json::from_str(&json_content)?; + + Ok(data) +} + +pub fn ensure_directory_exists(dir: &str) { + let path = Path::new(dir); + + if !path.exists() { + fs::create_dir_all(path).expect("Failed to create directory"); + println!("Directory created: {}", dir); + } else { + println!("Directory already exists: {}", dir); + } +} diff --git a/efc/src/validator.rs b/efc/src/validator.rs new file mode 100644 index 00000000..f0c7ac26 --- /dev/null +++ b/efc/src/validator.rs @@ -0,0 +1,105 @@ +use circuit_std_rs::poseidon_m31::*; +use expander_compiler::frontend::*; +use serde::Deserialize; + +use crate::utils::read_from_json_file; + +#[derive(Debug, Deserialize, Clone)] +pub struct ValidatorPlain { + #[serde(default)] + pub public_key: String, + #[serde(default)] + pub withdrawal_credentials: String, + #[serde(default)] + pub effective_balance: u64, + #[serde(default)] + pub slashed: bool, + #[serde(default)] + pub activation_eligibility_epoch: u64, + #[serde(default)] + pub activation_epoch: u64, + #[serde(default)] + pub exit_epoch: u64, + #[serde(default)] + pub withdrawable_epoch: u64, +} +pub fn read_validators(dir: &str) -> Vec { + let file_path = format!("{}/validatorList.json", dir); + let validaotrs: Vec = read_from_json_file(&file_path).unwrap(); + validaotrs +} + +#[derive(Clone, Copy)] +pub struct ValidatorSSZ { + pub public_key: [Variable; 48], + pub withdrawal_credentials: [Variable; 32], + pub effective_balance: [Variable; 8], + pub slashed: [Variable; 1], + pub activation_eligibility_epoch: [Variable; 8], + pub activation_epoch: [Variable; 8], + pub exit_epoch: [Variable; 8], + pub withdrawable_epoch: [Variable; 8], +} +impl Default for ValidatorSSZ { + fn default() -> Self { + Self { + public_key: [Variable::default(); 48], + withdrawal_credentials: [Variable::default(); 32], + effective_balance: [Variable::default(); 8], + slashed: [Variable::default(); 1], + activation_eligibility_epoch: [Variable::default(); 8], + activation_epoch: [Variable::default(); 8], + exit_epoch: [Variable::default(); 8], + withdrawable_epoch: [Variable::default(); 8], + } + } +} +impl ValidatorSSZ { + pub fn new() -> Self { + Self { + public_key: [Variable::default(); 48], + withdrawal_credentials: [Variable::default(); 32], + effective_balance: [Variable::default(); 8], + slashed: [Variable::default(); 1], + activation_eligibility_epoch: [Variable::default(); 8], + activation_epoch: [Variable::default(); 8], + exit_epoch: [Variable::default(); 8], + withdrawable_epoch: [Variable::default(); 8], + } + } + pub fn hash>(&self, builder: &mut B) -> Vec { + let mut inputs = Vec::new(); + for i in 0..48 { + inputs.push(self.public_key[i]); + } + for i in 0..32 { + inputs.push(self.withdrawal_credentials[i]); + } + for i in 0..8 { + inputs.push(self.effective_balance[i]); + } + for i in 0..1 { + inputs.push(self.slashed[i]); + } + for i in 0..8 { + inputs.push(self.activation_eligibility_epoch[i]); + } + for i in 0..8 { + inputs.push(self.activation_epoch[i]); + } + for i in 0..8 { + inputs.push(self.exit_epoch[i]); + } + for i in 0..8 { + inputs.push(self.withdrawable_epoch[i]); + } + let params = PoseidonM31Params::new( + builder, + POSEIDON_M31X16_RATE, + 16, + POSEIDON_M31X16_FULL_ROUNDS, + POSEIDON_M31X16_PARTIAL_ROUNDS, + ); + params.hash_to_state_flatten(builder, &inputs) + } +} diff --git a/expander_compiler/bin/trivial_circuit.rs b/expander_compiler/bin/trivial_circuit.rs index 07b0bc3f..1035a820 100644 --- a/expander_compiler/bin/trivial_circuit.rs +++ b/expander_compiler/bin/trivial_circuit.rs @@ -6,12 +6,15 @@ use ark_std::test_rng; use clap::Parser; +use expander_compiler::compile::CompileOptions; use expander_compiler::field::Field; -use expander_compiler::frontend::{compile, BN254Config, CompileResult, Define, M31Config}; +use expander_compiler::frontend::{ + compile, BN254Config, CompileResult, Define, M31Config, RootAPI, +}; use expander_compiler::utils::serde::Serde; use expander_compiler::{ declare_circuit, - frontend::{BasicAPI, Config, Variable, API}, + frontend::{Config, Variable}, }; /// Arguments for the command line @@ -42,7 +45,8 @@ fn main() { fn build() { let assignment = TrivialCircuit::::random_witnesses(); - let compile_result = compile::(&TrivialCircuit::new()).unwrap(); + let compile_result = + compile::(&TrivialCircuit::new(), CompileOptions::default()).unwrap(); let CompileResult { witness_solver, @@ -77,15 +81,15 @@ declare_circuit!(TrivialCircuit { }); impl Define for TrivialCircuit { - fn define(&self, builder: &mut API) { - let out = compute_output::(builder, &self.input_layer); + fn define>(&self, api: &mut Builder) { + let out = compute_output::(api, &self.input_layer); out.iter().zip(self.output_layer.iter()).for_each(|(x, y)| { - builder.assert_is_equal(x, y); + api.assert_is_equal(x, y); }); } } -fn compute_output(api: &mut API, input_layer: &[Variable]) -> Vec { +fn compute_output(api: &mut impl RootAPI, input_layer: &[Variable]) -> Vec { let mut cur_layer = input_layer.to_vec(); (0..NUM_LAYERS).for_each(|_| { diff --git a/expander_compiler/src/circuit/ir/expr.rs b/expander_compiler/src/circuit/ir/expr.rs index d6724091..ebf4204c 100644 --- a/expander_compiler/src/circuit/ir/expr.rs +++ b/expander_compiler/src/circuit/ir/expr.rs @@ -4,10 +4,8 @@ use std::{ ops::{Deref, DerefMut}, }; -use arith::FieldForECC; - use crate::circuit::config::Config; -use crate::field::FieldArith; +use crate::field::{FieldArith, FieldModulus}; use crate::utils::serde::Serde; #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -440,6 +438,13 @@ impl LinComb { } res } + pub fn eval_simd>(&self, values: &[SF]) -> SF { + let mut res = SF::one().scale(&self.constant); + for term in self.terms.iter() { + res += values[term.var].scale(&term.coef); + } + res + } } impl fmt::Display for LinComb { diff --git a/expander_compiler/src/circuit/ir/hint_normalized/mod.rs b/expander_compiler/src/circuit/ir/hint_normalized/mod.rs index 408a5e5f..91fa93c2 100644 --- a/expander_compiler/src/circuit/ir/hint_normalized/mod.rs +++ b/expander_compiler/src/circuit/ir/hint_normalized/mod.rs @@ -230,6 +230,11 @@ impl Instruction { Err(e) => EvalResult::Error(e), }; } + if let Instruction::CustomGate { .. } = self { + return EvalResult::Error(Error::UserError( + "CustomGate currently unsupported".to_string(), + )); + } self.eval_unsafe(values) } } @@ -523,4 +528,111 @@ impl RootCircuit { } Ok(res) } + + pub fn eval_safe_simd>( + &self, + inputs: Vec, + public_inputs: &[SF], + hint_caller: &mut impl HintCaller, + ) -> Result, Error> { + assert_eq!(inputs.len(), self.input_size()); + let mut result_values = Vec::new(); + self.eval_sub_safe_simd( + &self.circuits[&0], + inputs, + public_inputs, + hint_caller, + &mut result_values, + )?; + Ok(result_values) + } + + fn eval_sub_safe_simd>( + &self, + circuit: &Circuit, + inputs: Vec, + public_inputs: &[SF], + hint_caller: &mut impl HintCaller, + result_values: &mut Vec, + ) -> Result<(), Error> { + let mut values = vec![SF::zero(); 1]; + values.extend(inputs); + for insn in circuit.instructions.iter() { + match insn { + Instruction::LinComb(lc) => { + let res = lc.eval_simd(&values); + values.push(res); + } + Instruction::Mul(inputs) => { + let mut res = values[inputs[0]]; + for &i in inputs.iter().skip(1) { + res *= values[i]; + } + values.push(res); + } + Instruction::Hint { + hint_id, + inputs, + num_outputs, + } => { + let mut inputs_scalar = vec![Vec::with_capacity(inputs.len()); SF::PACK_SIZE]; + for x in inputs.iter().map(|i| values[*i]) { + let tmp = x.unpack(); + for (i, y) in tmp.iter().enumerate() { + inputs_scalar[i].push(*y); + } + } + let mut outputs_tmp = + vec![C::CircuitField::zero(); num_outputs * SF::PACK_SIZE]; + for (i, inputs) in inputs_scalar.iter().enumerate() { + let outputs = + match hints::safe_impl(hint_caller, *hint_id, inputs, *num_outputs) { + Ok(outputs) => outputs, + Err(e) => return Err(e), + }; + for (j, x) in outputs.iter().enumerate() { + outputs_tmp[j * SF::PACK_SIZE + i] = *x; + } + } + for i in 0..*num_outputs { + values.push(SF::pack( + &outputs_tmp[i * SF::PACK_SIZE..(i + 1) * SF::PACK_SIZE], + )); + } + } + Instruction::ConstantLike(coef) => { + let res = match coef { + Coef::Constant(c) => SF::one().scale(c), + Coef::PublicInput(i) => public_inputs[*i], + Coef::Random => { + return Err(Error::UserError( + "random coef occured in witness solver".to_string(), + )) + } + }; + values.push(res); + } + Instruction::SubCircuitCall { + sub_circuit_id, + inputs, + .. + } => { + self.eval_sub_safe_simd( + &self.circuits[sub_circuit_id], + inputs.iter().map(|&i| values[i]).collect(), + public_inputs, + hint_caller, + &mut values, + )?; + } + Instruction::CustomGate { .. } => { + panic!("CustomGate currently unsupported"); + } + } + } + for &o in circuit.outputs.iter() { + result_values.push(values[o]); + } + Ok(()) + } } diff --git a/expander_compiler/src/circuit/ir/hint_normalized/tests.rs b/expander_compiler/src/circuit/ir/hint_normalized/tests.rs index 7298b88d..97b02a1b 100644 --- a/expander_compiler/src/circuit/ir/hint_normalized/tests.rs +++ b/expander_compiler/src/circuit/ir/hint_normalized/tests.rs @@ -1,10 +1,11 @@ use rand::{Rng, RngCore}; +use arith::SimdField; + use super::{ Instruction::{self, ConstantLike, LinComb, Mul}, RootCircuit, }; -use crate::field::FieldArith; use crate::{ circuit::{ config::{Config, M31Config as C}, @@ -13,8 +14,10 @@ use crate::{ }, hints, }; +use crate::{field::FieldArith, hints::registry::StubHintCaller}; type CField = ::CircuitField; +type SF = mersenne31::M31x16; #[test] fn remove_hints_simple() { @@ -271,3 +274,60 @@ fn remove_and_export_random_2() { assert_eq!(cond1, cond2); } } + +#[test] +fn eval_simd_random() { + let mut config = RandomCircuitConfig { + seed: 0, + num_circuits: RandomRange { min: 1, max: 10 }, + num_inputs: RandomRange { min: 1, max: 10 }, + num_instructions: RandomRange { min: 1, max: 10 }, + num_constraints: RandomRange { min: 0, max: 10 }, + num_outputs: RandomRange { min: 1, max: 10 }, + num_terms: RandomRange { min: 1, max: 5 }, + sub_circuit_prob: 0.5, + }; + for i in 0..3000 { + config.seed = i + 10000; + let root = RootCircuit::::random(&config); + assert_eq!(root.validate(), Ok(())); + let mut inputs = vec![Vec::new(); SF::PACK_SIZE]; + let mut inputs_simd = Vec::new(); + for _ in 0..root.input_size() { + let tmp: Vec = (0..SF::PACK_SIZE) + .map(|_| CField::random_unsafe(&mut rand::thread_rng())) + .collect(); + for (x, y) in tmp.iter().zip(inputs.iter_mut()) { + y.push(*x); + } + inputs_simd.push(SF::pack(&tmp)); + } + let mut public_inputs = vec![Vec::new(); SF::PACK_SIZE]; + let mut public_inputs_simd = Vec::new(); + for _ in 0..root.num_public_inputs { + let tmp: Vec = (0..SF::PACK_SIZE) + .map(|_| CField::random_unsafe(&mut rand::thread_rng())) + .collect(); + for (x, y) in tmp.iter().zip(public_inputs.iter_mut()) { + y.push(*x); + } + public_inputs_simd.push(SF::pack(&tmp)); + } + let mut outputs = Vec::new(); + for i in 0..SF::PACK_SIZE { + let cur_outputs = root + .eval_safe(inputs[i].clone(), &public_inputs[i], &mut StubHintCaller) + .unwrap(); + outputs.push(cur_outputs); + } + let mut expected_outputs_simd = Vec::new(); + for i in 0..outputs[0].len() { + let tmp: Vec = outputs.iter().map(|x| x[i]).collect(); + expected_outputs_simd.push(SF::pack(&tmp)); + } + let outputs_simd = root + .eval_safe_simd(inputs_simd, &public_inputs_simd, &mut StubHintCaller) + .unwrap(); + assert_eq!(outputs_simd, expected_outputs_simd); + } +} diff --git a/expander_compiler/src/circuit/ir/hint_normalized/witness_solver.rs b/expander_compiler/src/circuit/ir/hint_normalized/witness_solver.rs index 77473faa..0c2504ad 100644 --- a/expander_compiler/src/circuit/ir/hint_normalized/witness_solver.rs +++ b/expander_compiler/src/circuit/ir/hint_normalized/witness_solver.rs @@ -1,4 +1,9 @@ -use crate::{circuit::layered::witness::Witness, utils::serde::Serde}; +use crate::{ + circuit::layered::witness::{Witness, WitnessValues}, + utils::serde::Serde, +}; + +use arith::SimdField; use super::*; @@ -33,7 +38,7 @@ impl WitnessSolver { num_witnesses: 1, num_inputs_per_witness, num_public_inputs_per_witness: self.circuit.num_public_inputs, - values, + values: WitnessValues::Scalar(values), }) } @@ -47,17 +52,54 @@ impl WitnessSolver { ) -> Result, Error> { let mut values = Vec::new(); let mut num_inputs_per_witness = 0; - for i in 0..num_witnesses { - let (a, b) = f(i); - let (a, num) = self.solve_witness_inner(a, b, hint_caller)?; - values.extend(a); - num_inputs_per_witness = num; + let pack_size = C::DefaultSimdField::PACK_SIZE; + let num_blocks = (num_witnesses + pack_size - 1) / pack_size; + for j in 0..num_blocks { + let i_start = j * pack_size; + let i_end = num_witnesses.min((j + 1) * pack_size); + let b_end = (j + 1) * pack_size; + let mut tmp_inputs = Vec::new(); + let mut tmp_public_inputs = Vec::new(); + for i in i_start..i_end { + let (a, b) = f(i); + assert_eq!(a.len(), self.circuit.input_size()); + assert_eq!(b.len(), self.circuit.num_public_inputs); + tmp_inputs.push(a); + tmp_public_inputs.push(b); + } + let mut simd_inputs = Vec::with_capacity(self.circuit.input_size()); + let mut simd_public_inputs = Vec::with_capacity(self.circuit.num_public_inputs); + let mut tmp: Vec = vec![C::CircuitField::zero(); pack_size]; + for k in 0..self.circuit.input_size() { + for i in i_start..i_end { + tmp[i - i_start] = tmp_inputs[i - i_start][k]; + } + for i in i_end..b_end { + tmp[i - i_start] = tmp[i - i_start - 1]; + } + simd_inputs.push(C::DefaultSimdField::pack(&tmp)); + } + for k in 0..self.circuit.num_public_inputs { + for i in i_start..i_end { + tmp[i - i_start] = tmp_public_inputs[i - i_start][k]; + } + for i in i_end..b_end { + tmp[i - i_start] = tmp[i - i_start - 1]; + } + simd_public_inputs.push(C::DefaultSimdField::pack(&tmp)); + } + let simd_result = + self.circuit + .eval_safe_simd(simd_inputs, &simd_public_inputs, hint_caller)?; + num_inputs_per_witness = simd_result.len(); + values.extend(simd_result); + values.extend(simd_public_inputs); } Ok(Witness { num_witnesses, num_inputs_per_witness, num_public_inputs_per_witness: self.circuit.num_public_inputs, - values, + values: WitnessValues::Simd(values), }) } } diff --git a/expander_compiler/src/circuit/layered/mod.rs b/expander_compiler/src/circuit/layered/mod.rs index 72472950..f4db6e1f 100644 --- a/expander_compiler/src/circuit/layered/mod.rs +++ b/expander_compiler/src/circuit/layered/mod.rs @@ -56,6 +56,22 @@ impl Coef { } } + pub fn get_value_with_public_inputs_simd>( + &self, + public_inputs: &[SF], + ) -> SF { + match self { + Coef::Constant(c) => SF::one().scale(c), + Coef::Random => SF::random_unsafe(&mut rand::thread_rng()), + Coef::PublicInput(id) => { + if *id >= public_inputs.len() { + panic!("public input id {} out of range", id); + } + public_inputs[*id] + } + } + } + pub fn validate(&self, num_public_inputs: usize) -> Result<(), Error> { match self { Coef::Constant(_) => Ok(()), @@ -801,6 +817,106 @@ impl Circuit { } } + pub fn eval_with_public_inputs_simd>( + &self, + inputs: Vec, + public_inputs: &[SF], + ) -> (Vec, Vec) { + if inputs.len() != self.input_size() { + panic!("input length mismatch"); + } + let mut cur = vec![inputs]; + for id in self.layer_ids.iter() { + let mut next = vec![SF::zero(); self.segments[*id].num_outputs]; + let mut inputs: Vec<&[SF]> = Vec::new(); + for i in 0..self.segments[*id].num_inputs.len() { + inputs.push(&cur[cur.len() - i - 1]); + } + self.apply_segment_with_public_inputs_simd( + &self.segments[*id], + &inputs, + &mut next, + public_inputs, + ); + cur.push(next); + } + let cur = cur.last().unwrap(); + let mut constraints_satisfied = vec![true; SF::PACK_SIZE]; + for out in cur.iter().take(self.expected_num_output_zeroes) { + let tmp = out.unpack(); + for i in 0..SF::PACK_SIZE { + if !tmp[i].is_zero() { + constraints_satisfied[i] = false; + } + } + } + ( + cur[self.expected_num_output_zeroes..self.num_actual_outputs].to_vec(), + constraints_satisfied, + ) + } + + fn apply_segment_with_public_inputs_simd>( + &self, + seg: &Segment, + cur: &[&[SF]], + nxt: &mut [SF], + public_inputs: &[SF], + ) { + for m in seg.gate_muls.iter() { + nxt[m.output] += cur[m.inputs[0].layer()][m.inputs[0].offset()] + * cur[m.inputs[1].layer()][m.inputs[1].offset()] + * m.coef.get_value_with_public_inputs_simd(public_inputs); + } + for a in seg.gate_adds.iter() { + nxt[a.output] += cur[a.inputs[0].layer()][a.inputs[0].offset()] + * a.coef.get_value_with_public_inputs_simd(public_inputs); + } + for cs in seg.gate_consts.iter() { + nxt[cs.output] += cs.coef.get_value_with_public_inputs_simd(public_inputs); + } + for cu in seg.gate_customs.iter() { + let mut inputs = vec![Vec::with_capacity(cu.inputs.len()); SF::PACK_SIZE]; + for input in cu.inputs.iter() { + let tmp = cur[input.layer()][input.offset()].unpack(); + for i in 0..SF::PACK_SIZE { + inputs[i].push(tmp[i]); + } + } + let mut outputs = Vec::with_capacity(SF::PACK_SIZE); + for x in inputs.iter() { + outputs.push(hints::stub_impl(cu.gate_type, x, 1)); + } + for i in 0..outputs[0].len() { + let mut tmp = Vec::with_capacity(SF::PACK_SIZE); + for x in outputs.iter() { + tmp.push(x[i]); + } + let output = SF::pack(&tmp); + nxt[cu.output + i] += + output * cu.coef.get_value_with_public_inputs_simd(public_inputs); + } + } + for (sub_id, allocs) in seg.child_segs.iter() { + let subc = &self.segments[*sub_id]; + for a in allocs.iter() { + let inputs = a + .input_offset + .iter() + .zip(subc.num_inputs.iter()) + .enumerate() + .map(|(l, (off, len))| &cur[l][off..off + len]) + .collect::>(); + self.apply_segment_with_public_inputs_simd( + subc, + &inputs, + &mut nxt[a.output_offset..a.output_offset + subc.num_outputs], + public_inputs, + ); + } + } + } + pub fn sort_everything(&mut self) { for seg in self.segments.iter_mut() { seg.gate_muls.sort(); diff --git a/expander_compiler/src/circuit/layered/witness.rs b/expander_compiler/src/circuit/layered/witness.rs index ded1eaf5..048b3ee0 100644 --- a/expander_compiler/src/circuit/layered/witness.rs +++ b/expander_compiler/src/circuit/layered/witness.rs @@ -1,12 +1,206 @@ +use std::any::{Any, TypeId}; +use std::mem; + +use arith::SimdField; + use super::*; -use crate::{circuit::config::Config, field::FieldModulus, utils::serde::Serde}; +use crate::{ + circuit::config::Config, + field::{Field, FieldModulus}, + utils::serde::Serde, +}; -#[derive(Debug)] +#[derive(Clone, Debug)] +pub enum WitnessValues { + Scalar(Vec), + Simd(Vec), +} + +#[derive(Clone, Debug)] pub struct Witness { pub num_witnesses: usize, pub num_inputs_per_witness: usize, pub num_public_inputs_per_witness: usize, - pub values: Vec, + pub values: WitnessValues, +} + +fn unpack_block>( + s: &[SF], + a: usize, + b: usize, +) -> Vec<(Vec, Vec)> { + let pack_size = SF::PACK_SIZE; + let mut res = Vec::with_capacity(pack_size); + for _ in 0..pack_size { + res.push((Vec::with_capacity(a), Vec::with_capacity(b))); + } + for x in s.iter().take(a) { + let tmp = x.unpack(); + for j in 0..pack_size { + res[j].0.push(tmp[j]); + } + } + for x in s.iter().skip(a).take(b) { + let tmp = x.unpack(); + for j in 0..pack_size { + res[j].1.push(tmp[j]); + } + } + res +} + +fn pack_block>( + s: &[F], + a: usize, + b: usize, +) -> (Vec, Vec) { + let pack_size = SF::PACK_SIZE; + let mut res = Vec::with_capacity(a); + let mut res2 = Vec::with_capacity(b); + let s_size = (s.len() / (a + b)).min(pack_size); + for i in 0..a { + let mut tmp = Vec::with_capacity(pack_size); + for j in 0..s_size { + tmp.push(s[j * (a + b) + i]); + } + // fill the rest with the last element + for _ in s_size..pack_size { + tmp.push(s[(s_size - 1) * (a + b) + i]); + } + res.push(SF::pack(&tmp)); + } + for i in a..a + b { + let mut tmp = Vec::with_capacity(pack_size); + for j in 0..s_size { + tmp.push(s[j * (a + b) + i]); + } + // fill the rest with the last element + for _ in s_size..pack_size { + tmp.push(s[(s_size - 1) * (a + b) + i]); + } + res2.push(SF::pack(&tmp)); + } + (res, res2) +} + +fn use_simd(num_witnesses: usize) -> bool { + num_witnesses > 1 && C::DefaultSimdField::PACK_SIZE > 1 +} + +type UnpackedBlock = Vec<( + Vec<::CircuitField>, + Vec<::CircuitField>, +)>; + +pub struct WitnessIteratorScalar<'a, C: Config> { + witness: &'a Witness, + index: usize, + buf_unpacked: UnpackedBlock, +} + +impl<'a, C: Config> Iterator for WitnessIteratorScalar<'a, C> { + type Item = (Vec, Vec); + fn next(&mut self) -> Option { + if self.index >= self.witness.num_witnesses { + return None; + } + let a = self.witness.num_inputs_per_witness; + let b = self.witness.num_public_inputs_per_witness; + match &self.witness.values { + WitnessValues::Scalar(values) => { + let res = ( + values[self.index * (a + b)..self.index * (a + b) + a].to_vec(), + values[self.index * (a + b) + a..self.index * (a + b) + a + b].to_vec(), + ); + self.index += 1; + Some(res) + } + WitnessValues::Simd(values) => { + let pack_size = C::DefaultSimdField::PACK_SIZE; + if self.index % pack_size == 0 { + self.buf_unpacked = + unpack_block(&values[(self.index / pack_size) * (a + b)..], a, b); + } + let res = ( + mem::take(&mut self.buf_unpacked[self.index % pack_size].0), + mem::take(&mut self.buf_unpacked[self.index % pack_size].1), + ); + self.index += 1; + Some(res) + } + } + } +} + +pub struct WitnessIteratorSimd<'a, C: Config> { + witness: &'a Witness, + index: usize, +} + +impl<'a, C: Config> Iterator for WitnessIteratorSimd<'a, C> { + type Item = (Vec, Vec); + fn next(&mut self) -> Option { + let pack_size = C::DefaultSimdField::PACK_SIZE; + if self.index * pack_size >= self.witness.num_witnesses { + return None; + } + let a = self.witness.num_inputs_per_witness; + let b = self.witness.num_public_inputs_per_witness; + match &self.witness.values { + WitnessValues::Scalar(values) => { + let (inputs, public_inputs) = + pack_block(&values[self.index * pack_size * (a + b)..], a, b); + self.index += 1; + Some((inputs, public_inputs)) + } + WitnessValues::Simd(values) => { + let inputs = values[self.index * (a + b)..self.index * (a + b) + a].to_vec(); + let public_inputs = + values[self.index * (a + b) + a..self.index * (a + b) + a + b].to_vec(); + self.index += 1; + Some((inputs, public_inputs)) + } + } + } +} + +impl Witness { + pub fn iter_scalar(&self) -> WitnessIteratorScalar<'_, C> { + WitnessIteratorScalar { + witness: self, + index: 0, + buf_unpacked: Vec::new(), + } + } + + pub fn iter_simd(&self) -> WitnessIteratorSimd<'_, C> { + WitnessIteratorSimd { + witness: self, + index: 0, + } + } + + fn convert_to_simd(&mut self) { + let values = match &self.values { + WitnessValues::Scalar(values) => values, + WitnessValues::Simd(_) => { + return; + } + }; + let mut res = Vec::new(); + let a = self.num_inputs_per_witness + self.num_public_inputs_per_witness; + let pack_size = C::DefaultSimdField::PACK_SIZE; + let num_blocks = (self.num_witnesses + pack_size - 1) / pack_size; + for i in 0..num_blocks { + let tmp = pack_block::( + &values[i * pack_size * a..], + a, + 0, + ); + res.extend(tmp.0); + } + self.values = WitnessValues::Simd(res); + } } impl Circuit { @@ -14,24 +208,29 @@ impl Circuit { if witness.num_witnesses == 0 { panic!("expected at least 1 witness") } - let mut res = Vec::new(); - let a = witness.num_inputs_per_witness; - let b = witness.num_public_inputs_per_witness; - for i in 0..witness.num_witnesses { - let (_, out) = self.eval_with_public_inputs( - witness.values[i * (a + b)..i * (a + b) + a].to_vec(), - &witness.values[i * (a + b) + a..i * (a + b) + a + b], - ); - res.push(out); + if use_simd::(witness.num_witnesses) { + let mut res = Vec::new(); + for (inputs, public_inputs) in witness.iter_simd() { + let (_, out) = self.eval_with_public_inputs_simd(inputs, &public_inputs); + res.extend(out); + } + res.truncate(witness.num_witnesses); + res + } else { + let mut res = Vec::new(); + for (inputs, public_inputs) in witness.iter_scalar() { + let (_, out) = self.eval_with_public_inputs(inputs, &public_inputs); + res.push(out); + } + res } - res } } impl Witness { pub fn to_simd(&self) -> (Vec, Vec) where - T: arith::SimdField, + T: arith::SimdField + 'static, { match self.num_witnesses.cmp(&T::PACK_SIZE) { std::cmp::Ordering::Less => { @@ -50,23 +249,30 @@ impl Witness { } std::cmp::Ordering::Equal => {} } - let ni = self.num_inputs_per_witness; - let np = self.num_public_inputs_per_witness; - let mut res = Vec::with_capacity(ni); - let mut res_public = Vec::with_capacity(np); - for i in 0..ni + np { - let mut values: Vec = (0..self.num_witnesses.min(T::PACK_SIZE)) - .map(|j| self.values[j * (ni + np) + i]) - .collect(); - values.resize(T::PACK_SIZE, C::CircuitField::zero()); - let simd_value = T::pack(&values); - if i < ni { - res.push(simd_value); - } else { - res_public.push(simd_value); + let a = self.num_inputs_per_witness; + let b = self.num_public_inputs_per_witness; + match &self.values { + WitnessValues::Scalar(values) => pack_block(values, a, b), + WitnessValues::Simd(values) => { + if TypeId::of::() == TypeId::of::() { + let inputs = values[..a].to_vec(); + let public_inputs = values[a..a + b].to_vec(); + let tmp: Box = Box::new((inputs, public_inputs)); + match tmp.downcast::<(Vec, Vec)>() { + Ok(t) => { + return *t; + } + Err(_) => panic!("downcast failed"), + } + } + let mut tmp = Vec::new(); + for (x, y) in self.iter_scalar().take(T::PACK_SIZE) { + tmp.extend(x); + tmp.extend(y); + } + pack_block(&tmp, a, b) } } - (res, res_public) } } @@ -88,12 +294,16 @@ impl Serde for Witness { for _ in 0..num_witnesses * (num_inputs_per_witness + num_public_inputs_per_witness) { values.push(C::CircuitField::deserialize_from(&mut reader)?); } - Ok(Self { + let mut res = Self { num_witnesses, num_inputs_per_witness, num_public_inputs_per_witness, - values, - }) + values: WitnessValues::Scalar(values), + }; + if use_simd::(num_witnesses) { + res.convert_to_simd(); + } + Ok(res) } fn serialize_into(&self, mut writer: W) -> Result<(), std::io::Error> { self.num_witnesses.serialize_into(&mut writer)?; @@ -101,9 +311,55 @@ impl Serde for Witness { self.num_public_inputs_per_witness .serialize_into(&mut writer)?; C::CircuitField::MODULUS.serialize_into(&mut writer)?; - for v in &self.values { - v.serialize_into(&mut writer)?; + match &self.values { + WitnessValues::Scalar(values) => { + for v in values { + v.serialize_into(&mut writer)?; + } + } + WitnessValues::Simd(_) => { + for (a, b) in self.iter_scalar() { + for v in a { + v.serialize_into(&mut writer)?; + } + for v in b { + v.serialize_into(&mut writer)?; + } + } + } } Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::circuit::config::M31Config; + use crate::field::M31; + + #[test] + fn basic_simd() { + let n = 29; + let a = 17; + let b = 5; + let mut v = Vec::new(); + for _ in 0..n * (a + b) { + v.push(M31::random_unsafe(&mut rand::thread_rng())); + } + let w1: Witness = Witness { + num_witnesses: n, + num_inputs_per_witness: a, + num_public_inputs_per_witness: b, + values: WitnessValues::::Scalar(v), + }; + let mut w2 = w1.clone(); + w2.convert_to_simd(); + let w1_iv_sc = w1.iter_scalar().collect::>(); + let w2_iv_sc = w2.iter_scalar().collect::>(); + let w1_iv_sm = w1.iter_simd().collect::>(); + let w2_iv_sm = w2.iter_simd().collect::>(); + assert_eq!(w1_iv_sc, w2_iv_sc); + assert_eq!(w1_iv_sm, w2_iv_sm); + } +} diff --git a/expander_compiler/src/frontend/builder.rs b/expander_compiler/src/frontend/builder.rs index bc92c972..f397bca7 100644 --- a/expander_compiler/src/frontend/builder.rs +++ b/expander_compiler/src/frontend/builder.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::convert::From; use ethnum::U256; use tiny_keccak::Hasher; @@ -42,10 +43,29 @@ pub fn new_variable(id: usize) -> Variable { Variable { id } } +// impl Variable for From trait +impl From for Variable { + fn from(id: usize) -> Self { + Variable { id } + } +} + pub fn get_variable_id(v: Variable) -> usize { v.id } +pub fn ensure_variable_valid(v: Variable) { + if v.id == 0 { + panic!("Variable(0) is not allowed in API calls"); + } +} + +pub fn ensure_variables_valid(vs: &[Variable]) { + for v in vs { + ensure_variable_valid(*v); + } +} + pub enum VariableOrValue { Variable(Variable), Value(F), @@ -68,13 +88,18 @@ impl + NotVariable + Clone> ToVariableOrValue for T { impl ToVariableOrValue for Variable { fn convert_to_variable_or_value(self) -> VariableOrValue { + // In almost all API functions, the argument is impl ToVariableOrValue. + // (Actually it's all but new_hint and memorized_simple_call) + // We need to prevent invalid (default) Variables from passing into the functions. + // And here's the best location to do it. + ensure_variable_valid(self); VariableOrValue::Variable(self) } } impl ToVariableOrValue for &Variable { fn convert_to_variable_or_value(self) -> VariableOrValue { - VariableOrValue::Variable(*self) + (*self).convert_to_variable_or_value() } } @@ -391,6 +416,7 @@ impl BasicAPI for Builder { inputs: &[Variable], num_outputs: usize, ) -> Vec { + ensure_variables_valid(inputs); self.instructions.push(SourceInstruction::Hint { hint_id: hint_key_to_id(hint_key), inputs: inputs.iter().map(|v| v.id).collect(), @@ -580,6 +606,7 @@ impl RootAPI for RootBuilder { f: F, inputs: &[Variable], ) -> Vec { + ensure_variables_valid(inputs); let mut hasher = tiny_keccak::Keccak::v256(); hasher.update(b"simple"); hasher.update(&inputs.len().to_le_bytes()); diff --git a/expander_compiler/src/frontend/circuit.rs b/expander_compiler/src/frontend/circuit.rs index 90cd7d19..c24eb1d3 100644 --- a/expander_compiler/src/frontend/circuit.rs +++ b/expander_compiler/src/frontend/circuit.rs @@ -165,11 +165,7 @@ pub use declare_circuit_num_vars; use crate::circuit::config::Config; use super::api::RootAPI; -use super::builder::RootBuilder; -pub trait Define { - fn define(&self, api: &mut RootBuilder); -} -pub trait GenericDefine { +pub trait Define { fn define>(&self, api: &mut Builder); } diff --git a/expander_compiler/src/frontend/debug.rs b/expander_compiler/src/frontend/debug.rs index 0b97b111..250d452e 100644 --- a/expander_compiler/src/frontend/debug.rs +++ b/expander_compiler/src/frontend/debug.rs @@ -12,7 +12,9 @@ use crate::{ use super::{ api::{BasicAPI, RootAPI, UnconstrainedAPI}, - builder::{get_variable_id, new_variable, ToVariableOrValue, VariableOrValue}, + builder::{ + ensure_variables_valid, get_variable_id, new_variable, ToVariableOrValue, VariableOrValue, + }, Variable, }; @@ -133,6 +135,7 @@ impl> BasicAPI for DebugBuilder Vec { + ensure_variables_valid(inputs); let inputs: Vec = inputs.iter().map(|v| self.convert_to_value(v)).collect(); match self @@ -405,6 +408,7 @@ impl> RootAPI for DebugBuilder Vec { + ensure_variables_valid(inputs); let inputs = inputs.to_vec(); f(self, &inputs) } diff --git a/expander_compiler/src/frontend/mod.rs b/expander_compiler/src/frontend/mod.rs index 761ead61..013f5512 100644 --- a/expander_compiler/src/frontend/mod.rs +++ b/expander_compiler/src/frontend/mod.rs @@ -19,7 +19,7 @@ pub use crate::hints::registry::{EmptyHintCaller, HintCaller, HintRegistry}; pub use crate::utils::error::Error; pub use api::{BasicAPI, RootAPI}; pub use builder::Variable; -pub use circuit::{Define, GenericDefine}; +pub use circuit::Define; pub use witness::WitnessSolver; pub mod internal { @@ -41,7 +41,7 @@ pub mod extra { pub fn debug_eval< C: Config, - Cir: internal::DumpLoadTwoVariables + GenericDefine + Clone, + Cir: internal::DumpLoadTwoVariables + Define + Clone, CA: internal::DumpLoadTwoVariables, H: HintCaller, >( @@ -69,20 +69,6 @@ pub mod extra { #[cfg(test)] mod tests; -fn build + Define + Clone>( - circuit: &Cir, -) -> ir::source::RootCircuit { - let (num_inputs, num_public_inputs) = circuit.num_vars(); - let (mut root_builder, input_variables, public_input_variables) = - RootBuilder::::new(num_inputs, num_public_inputs); - let mut circuit = circuit.clone(); - let mut vars_ptr = input_variables.as_slice(); - let mut public_vars_ptr = public_input_variables.as_slice(); - circuit.load_from(&mut vars_ptr, &mut public_vars_ptr); - circuit.define(&mut root_builder); - root_builder.build() -} - pub struct CompileResult { pub witness_solver: WitnessSolver, pub layered_circuit: layered::Circuit, @@ -93,21 +79,7 @@ pub struct CompileResultCrossLayer { pub layered_circuit: layered::Circuit, } -pub fn compile + Define + Clone>( - circuit: &Cir, -) -> Result, Error> { - let root = build(circuit); - let (irw, lc) = crate::compile::compile::(&root)?; - Ok(CompileResult { - witness_solver: WitnessSolver { circuit: irw }, - layered_circuit: lc, - }) -} - -fn build_generic< - C: Config, - Cir: internal::DumpLoadTwoVariables + GenericDefine + Clone, ->( +fn build + Define + Clone>( circuit: &Cir, ) -> ir::source::RootCircuit { let (num_inputs, num_public_inputs) = circuit.num_vars(); @@ -121,14 +93,11 @@ fn build_generic< root_builder.build() } -pub fn compile_generic< - C: Config, - Cir: internal::DumpLoadTwoVariables + GenericDefine + Clone, ->( +pub fn compile + Define + Clone>( circuit: &Cir, options: CompileOptions, ) -> Result, Error> { - let root = build_generic(circuit); + let root = build(circuit); let (irw, lc) = crate::compile::compile_with_options::(&root, options)?; Ok(CompileResult { witness_solver: WitnessSolver { circuit: irw }, @@ -136,14 +105,14 @@ pub fn compile_generic< }) } -pub fn compile_generic_cross_layer< +pub fn compile_cross_layer< C: Config, - Cir: internal::DumpLoadTwoVariables + GenericDefine + Clone, + Cir: internal::DumpLoadTwoVariables + Define + Clone, >( circuit: &Cir, options: CompileOptions, ) -> Result, Error> { - let root = build_generic(circuit); + let root = build(circuit); let (irw, lc) = crate::compile::compile_with_options::(&root, options)?; Ok(CompileResultCrossLayer { witness_solver: WitnessSolver { circuit: irw }, diff --git a/expander_compiler/src/frontend/tests.rs b/expander_compiler/src/frontend/tests.rs index 31bc9102..fd5455a8 100644 --- a/expander_compiler/src/frontend/tests.rs +++ b/expander_compiler/src/frontend/tests.rs @@ -1,15 +1,11 @@ use crate::{ circuit::config::M31Config, + compile::CompileOptions, field::{FieldArith, M31}, + frontend::{compile, RootAPI}, }; -use super::{ - api::BasicAPI, - builder::{RootBuilder, Variable}, - circuit::*, - compile, - variables::DumpLoadTwoVariables, -}; +use super::{builder::Variable, circuit::*, variables::DumpLoadTwoVariables}; declare_circuit!(Circuit1 { a: Variable, @@ -60,7 +56,7 @@ declare_circuit!(Circuit2 { }); impl Define for Circuit2 { - fn define(&self, builder: &mut RootBuilder) { + fn define>(&self, builder: &mut Builder) { let sum = builder.add(self.x[0], self.x[1]); let sum = builder.add(sum, 123); builder.assert_is_equal(sum, self.sum); @@ -69,7 +65,7 @@ impl Define for Circuit2 { #[test] fn test_circuit_eval_simple() { - let compile_result = compile(&Circuit2::default()).unwrap(); + let compile_result = compile(&Circuit2::default(), CompileOptions::default()).unwrap(); let assignment = Circuit2:: { sum: M31::from(126), x: [M31::from(1), M31::from(2)], diff --git a/expander_compiler/src/hints/registry.rs b/expander_compiler/src/hints/registry.rs index 27ee0833..10494291 100644 --- a/expander_compiler/src/hints/registry.rs +++ b/expander_compiler/src/hints/registry.rs @@ -4,7 +4,7 @@ use tiny_keccak::Hasher; use crate::{field::Field, utils::error::Error}; -use super::BuiltinHintIds; +use super::{stub_impl, BuiltinHintIds}; pub type HintFn = dyn FnMut(&[F], &mut [F]) -> Result<(), Error>; @@ -59,6 +59,7 @@ impl EmptyHintCaller { Self } } +pub struct StubHintCaller; pub trait HintCaller: 'static { fn call(&mut self, id: usize, args: &[F], num_outputs: usize) -> Result, Error>; @@ -75,3 +76,9 @@ impl HintCaller for EmptyHintCaller { Err(Error::UserError(format!("hint with id {} not found", id))) } } + +impl HintCaller for StubHintCaller { + fn call(&mut self, id: usize, args: &[F], num_outputs: usize) -> Result, Error> { + Ok(stub_impl(id, &args.to_vec(), num_outputs)) + } +} diff --git a/expander_compiler/tests/example.rs b/expander_compiler/tests/example.rs index bccac8f0..3a438bb4 100644 --- a/expander_compiler/tests/example.rs +++ b/expander_compiler/tests/example.rs @@ -7,14 +7,14 @@ declare_circuit!(Circuit { }); impl Define for Circuit { - fn define(&self, builder: &mut API) { - builder.assert_is_equal(self.x, self.y); + fn define>(&self, api: &mut Builder) { + api.assert_is_equal(self.x, self.y); } } #[test] fn example_full() { - let compile_result = compile(&Circuit::default()).unwrap(); + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); let assignment = Circuit:: { x: M31::from(123), y: M31::from(123), diff --git a/expander_compiler/tests/example_call_expander.rs b/expander_compiler/tests/example_call_expander.rs index 198744d8..f65b5027 100644 --- a/expander_compiler/tests/example_call_expander.rs +++ b/expander_compiler/tests/example_call_expander.rs @@ -8,19 +8,20 @@ declare_circuit!(Circuit { }); impl Define for Circuit { - fn define(&self, builder: &mut API) { - let mut sum = builder.constant(0); + fn define>(&self, api: &mut Builder) { + let mut sum = api.constant(0); for x in self.s.iter() { - sum = builder.add(sum, x); + sum = api.add(sum, x); } - builder.assert_is_equal(sum, self.sum); + api.assert_is_equal(sum, self.sum); } } fn example() { let n_witnesses = ::PACK_SIZE; println!("n_witnesses: {}", n_witnesses); - let compile_result: CompileResult = compile(&Circuit::default()).unwrap(); + let compile_result: CompileResult = + compile(&Circuit::default(), CompileOptions::default()).unwrap(); let mut s = [C::CircuitField::zero(); 100]; let mut rng = rand::rngs::StdRng::seed_from_u64(1235); for i in 0..s.len() { diff --git a/expander_compiler/tests/keccak_gf2.rs b/expander_compiler/tests/keccak_gf2.rs index 445c3732..74417ff9 100644 --- a/expander_compiler/tests/keccak_gf2.rs +++ b/expander_compiler/tests/keccak_gf2.rs @@ -215,7 +215,7 @@ fn compute_keccak>(api: &mut B, p: &Vec) -> V copy_out_unaligned(ss, 136, 32) } -impl GenericDefine for Keccak256Circuit { +impl Define for Keccak256Circuit { fn define>(&self, api: &mut Builder) { for i in 0..N_HASHES { // You can use api.memorized_simple_call for sub-circuits @@ -308,8 +308,7 @@ fn keccak_gf2_test( #[test] fn keccak_gf2_main() { - let compile_result = - compile_generic(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); + let compile_result = compile(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); let CompileResult { witness_solver, layered_circuit, @@ -320,8 +319,7 @@ fn keccak_gf2_main() { #[test] fn keccak_gf2_main_cross_layer() { let compile_result = - compile_generic_cross_layer(&Keccak256Circuit::default(), CompileOptions::default()) - .unwrap(); + compile_cross_layer(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); let CompileResultCrossLayer { witness_solver, layered_circuit, diff --git a/expander_compiler/tests/keccak_gf2_full.rs b/expander_compiler/tests/keccak_gf2_full.rs index 973c6f68..9568c24f 100644 --- a/expander_compiler/tests/keccak_gf2_full.rs +++ b/expander_compiler/tests/keccak_gf2_full.rs @@ -34,7 +34,7 @@ fn rc() -> Vec { } fn xor_in( - api: &mut API, + api: &mut impl RootAPI, mut s: Vec>, buf: Vec>, ) -> Vec> { @@ -48,7 +48,7 @@ fn xor_in( s } -fn keccak_f(api: &mut API, mut a: Vec>) -> Vec> { +fn keccak_f(api: &mut impl RootAPI, mut a: Vec>) -> Vec> { let mut b = vec![vec![api.constant(0); 64]; 25]; let mut c = vec![vec![api.constant(0); 64]; 5]; let mut d = vec![vec![api.constant(0); 64]; 5]; @@ -132,7 +132,7 @@ fn keccak_f(api: &mut API, mut a: Vec>) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn xor(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -141,7 +141,7 @@ fn xor(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn and(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -150,7 +150,7 @@ fn and(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec) -> Vec { +fn not(api: &mut impl RootAPI, a: Vec) -> Vec { let mut bits_res = vec![api.constant(0); a.len()]; for i in 0..a.len() { bits_res[i] = api.sub(1, a[i].clone()); @@ -188,7 +188,7 @@ declare_circuit!(Keccak256Circuit { out: [[PublicVariable; 256]; N_HASHES], }); -fn compute_keccak(api: &mut API, p: &Vec) -> Vec { +fn compute_keccak(api: &mut impl RootAPI, p: &Vec) -> Vec { let mut ss = vec![vec![api.constant(0); 64]; 25]; let mut new_p = p.clone(); let mut append_data = vec![0; 136 - 64]; @@ -211,7 +211,7 @@ fn compute_keccak(api: &mut API, p: &Vec) -> Vec for Keccak256Circuit { - fn define(&self, api: &mut API) { + fn define>(&self, api: &mut Builder) { for i in 0..N_HASHES { // You can use api.memorized_simple_call for sub-circuits // let out = api.memorized_simple_call(compute_keccak, &self.p[i].to_vec()); @@ -225,7 +225,7 @@ impl Define for Keccak256Circuit { #[test] fn keccak_gf2_full() { - let compile_result = compile(&Keccak256Circuit::default()).unwrap(); + let compile_result = compile(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); let CompileResult { witness_solver, layered_circuit, diff --git a/expander_compiler/tests/keccak_gf2_full_crosslayer.rs b/expander_compiler/tests/keccak_gf2_full_crosslayer.rs index 6e4bc6d4..eac2711a 100644 --- a/expander_compiler/tests/keccak_gf2_full_crosslayer.rs +++ b/expander_compiler/tests/keccak_gf2_full_crosslayer.rs @@ -214,7 +214,7 @@ fn compute_keccak>(api: &mut B, p: &Vec) -> V copy_out_unaligned(ss, 136, 32) } -impl GenericDefine for Keccak256Circuit { +impl Define for Keccak256Circuit { fn define>(&self, api: &mut Builder) { for i in 0..N_HASHES { // You can use api.memorized_simple_call for sub-circuits @@ -230,8 +230,7 @@ impl GenericDefine for Keccak256Circuit { #[test] fn keccak_gf2_full_crosslayer() { let compile_result = - compile_generic_cross_layer(&Keccak256Circuit::default(), CompileOptions::default()) - .unwrap(); + compile_cross_layer(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); let CompileResultCrossLayer { witness_solver, layered_circuit, diff --git a/expander_compiler/tests/keccak_gf2_vec.rs b/expander_compiler/tests/keccak_gf2_vec.rs index 207f1c87..30d13ef4 100644 --- a/expander_compiler/tests/keccak_gf2_vec.rs +++ b/expander_compiler/tests/keccak_gf2_vec.rs @@ -34,7 +34,7 @@ fn rc() -> Vec { } fn xor_in( - api: &mut API, + api: &mut impl RootAPI, mut s: Vec>, buf: Vec>, ) -> Vec> { @@ -48,7 +48,7 @@ fn xor_in( s } -fn keccak_f(api: &mut API, mut a: Vec>) -> Vec> { +fn keccak_f(api: &mut impl RootAPI, mut a: Vec>) -> Vec> { let mut b = vec![vec![api.constant(0); 64]; 25]; let mut c = vec![vec![api.constant(0); 64]; 5]; let mut d = vec![vec![api.constant(0); 64]; 5]; @@ -132,7 +132,7 @@ fn keccak_f(api: &mut API, mut a: Vec>) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn xor(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -141,7 +141,7 @@ fn xor(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn and(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -150,7 +150,7 @@ fn and(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec) -> Vec { +fn not(api: &mut impl RootAPI, a: Vec) -> Vec { let mut bits_res = vec![api.constant(0); a.len()]; for i in 0..a.len() { bits_res[i] = api.sub(1, a[i].clone()); @@ -188,7 +188,7 @@ declare_circuit!(Keccak256Circuit { out: [[PublicVariable]], }); -fn compute_keccak(api: &mut API, p: &Vec) -> Vec { +fn compute_keccak(api: &mut impl RootAPI, p: &Vec) -> Vec { let mut ss = vec![vec![api.constant(0); 64]; 25]; let mut new_p = p.clone(); let mut append_data = vec![0; 136 - 64]; @@ -211,7 +211,7 @@ fn compute_keccak(api: &mut API, p: &Vec) -> Vec for Keccak256Circuit { - fn define(&self, api: &mut API) { + fn define>(&self, api: &mut Builder) { for i in 0..N_HASHES { let out = api.memorized_simple_call(compute_keccak, &self.p[i].to_vec()); for j in 0..256 { @@ -227,7 +227,7 @@ fn keccak_gf2_vec() { circuit.p = vec![vec![Variable::default(); 64 * 8]; N_HASHES]; circuit.out = vec![vec![Variable::default(); 32 * 8]; N_HASHES]; - let compile_result = compile(&circuit).unwrap(); + let compile_result = compile(&circuit, CompileOptions::default()).unwrap(); let CompileResult { witness_solver, layered_circuit, @@ -271,7 +271,7 @@ fn keccak_gf2_vec() { println!("test 2 passed"); let mut assignments = Vec::new(); - for _ in 0..16 { + for _ in 0..15 { for k in 0..N_HASHES { assignment.p[k][0] = assignment.p[k][0] - GF2::from(1); } @@ -279,7 +279,7 @@ fn keccak_gf2_vec() { } let witness = witness_solver.solve_witnesses(&assignments).unwrap(); let res = layered_circuit.run(&witness); - let mut expected_res = vec![false; 16]; + let mut expected_res = vec![false; 15]; for i in 0..8 { expected_res[i * 2] = true; } diff --git a/expander_compiler/tests/keccak_m31_bn254.rs b/expander_compiler/tests/keccak_m31_bn254.rs index 686f862d..40de8012 100644 --- a/expander_compiler/tests/keccak_m31_bn254.rs +++ b/expander_compiler/tests/keccak_m31_bn254.rs @@ -54,7 +54,11 @@ fn compress_bits(b: Vec) -> Vec { res } -fn check_bits(api: &mut API, mut a: Vec, b_compressed: Vec) { +fn check_bits( + api: &mut impl RootAPI, + mut a: Vec, + b_compressed: Vec, +) { if a.len() != CHECK_BITS || C::CircuitField::FIELD_SIZE <= PARTITION_BITS { panic!("gg"); } @@ -72,7 +76,7 @@ fn check_bits(api: &mut API, mut a: Vec, b_compressed: V } } -fn from_my_bit_form(api: &mut API, x: Variable) -> Variable { +fn from_my_bit_form(api: &mut impl RootAPI, x: Variable) -> Variable { let t = api.sub(1, x); api.div(t, 2, true) } @@ -87,7 +91,7 @@ fn to_my_bit_form(x: usize) -> C::CircuitField { } fn xor_in( - api: &mut API, + api: &mut impl RootAPI, mut s: Vec>, buf: Vec>, ) -> Vec> { @@ -101,7 +105,7 @@ fn xor_in( s } -fn keccak_f(api: &mut API, mut a: Vec>) -> Vec> { +fn keccak_f(api: &mut impl RootAPI, mut a: Vec>) -> Vec> { let mut b = vec![vec![api.constant(0); 64]; 25]; let mut c = vec![vec![api.constant(0); 64]; 5]; let mut d = vec![vec![api.constant(0); 64]; 5]; @@ -185,7 +189,7 @@ fn keccak_f(api: &mut API, mut a: Vec>) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn xor(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -194,7 +198,7 @@ fn xor(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec, b: Vec) -> Vec { +fn and(api: &mut impl RootAPI, a: Vec, b: Vec) -> Vec { let nbits = a.len(); let mut bits_res = vec![api.constant(0); nbits]; for i in 0..nbits { @@ -208,7 +212,7 @@ fn and(api: &mut API, a: Vec, b: Vec) -> Vec(api: &mut API, a: Vec) -> Vec { +fn not(api: &mut impl RootAPI, a: Vec) -> Vec { let mut bits_res = vec![api.constant(0); a.len()]; for i in 0..a.len() { bits_res[i] = api.sub(0, a[i].clone()); @@ -246,7 +250,7 @@ declare_circuit!(Keccak256Circuit { out: [[PublicVariable; CHECK_PARTITIONS]; N_HASHES], }); -fn compute_keccak(api: &mut API, p: &Vec) -> Vec { +fn compute_keccak(api: &mut impl RootAPI, p: &Vec) -> Vec { for x in p.iter() { let x_sqr = api.mul(x, x); api.assert_is_equal(x_sqr, 1); @@ -274,7 +278,7 @@ fn compute_keccak(api: &mut API, p: &Vec) -> Vec Define for Keccak256Circuit { - fn define(&self, api: &mut API) { + fn define>(&self, api: &mut Builder) { for i in 0..N_HASHES { // You can use api.memorized_simple_call for sub-circuits // let out = api.memorized_simple_call(compute_keccak, &self.p[i].to_vec()); @@ -285,7 +289,8 @@ impl Define for Keccak256Circuit { } fn keccak_big_field(field_name: &str) { - let compile_result: CompileResult = compile(&Keccak256Circuit::default()).unwrap(); + let compile_result: CompileResult = + compile(&Keccak256Circuit::default(), CompileOptions::default()).unwrap(); let CompileResult { witness_solver, layered_circuit, diff --git a/expander_compiler/tests/mul_fanout_limit.rs b/expander_compiler/tests/mul_fanout_limit.rs index dd4076b7..46d7fe61 100644 --- a/expander_compiler/tests/mul_fanout_limit.rs +++ b/expander_compiler/tests/mul_fanout_limit.rs @@ -6,7 +6,7 @@ declare_circuit!(Circuit { sum: Variable, }); -impl GenericDefine for Circuit { +impl Define for Circuit { fn define>(&self, builder: &mut Builder) { let mut sum = builder.constant(0); for i in 0..16 { @@ -20,7 +20,7 @@ impl GenericDefine for Circuit { } fn mul_fanout_limit(limit: usize) { - let compile_result = compile_generic( + let compile_result = compile( &Circuit::default(), CompileOptions::default().with_mul_fanout_limit(limit), ) diff --git a/expander_compiler/tests/multithreading_witness.rs b/expander_compiler/tests/multithreading_witness.rs new file mode 100644 index 00000000..e4300b9e --- /dev/null +++ b/expander_compiler/tests/multithreading_witness.rs @@ -0,0 +1,48 @@ +use std::{sync::Arc, thread}; + +use expander_compiler::frontend::*; + +declare_circuit!(Circuit { + x: Variable, + y: Variable, +}); + +impl Define for Circuit { + fn define>(&self, builder: &mut Builder) { + builder.assert_is_equal(self.x, self.y); + } +} + +#[test] +fn multithreading_witness_solving() { + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); + let mut assignments = Vec::new(); + for _ in 0..1024 { + assignments.push(Circuit:: { + x: M31::from(123), + y: M31::from(123), + }); + } + // Since our SimdField is M31x16, we can solve 16 assignments at once + let assignment_chunks: Vec>> = + assignments.chunks(16).map(|x| x.to_vec()).collect(); + // We use Arc to share the WitnessSolver between threads + let witness_solver = Arc::new(compile_result.witness_solver); + // In this example, we start a thread for each chunk of assignments + // You may use a thread pool for better performance + let handles = assignment_chunks + .into_iter() + .map(|assignments| { + let witness_solver = Arc::clone(&witness_solver); + thread::spawn(move || witness_solver.solve_witnesses(&assignments).unwrap()) + }) + .collect::>(); + let mut results = Vec::new(); + for handle in handles { + results.push(handle.join().unwrap()); + } + for result in results { + let output = compile_result.layered_circuit.run(&result); + assert_eq!(output, vec![true; 16]); + } +} diff --git a/expander_compiler/tests/simple_add_m31.rs b/expander_compiler/tests/simple_add_m31.rs index 8a7fcf5a..8da0ebab 100644 --- a/expander_compiler/tests/simple_add_m31.rs +++ b/expander_compiler/tests/simple_add_m31.rs @@ -6,7 +6,7 @@ declare_circuit!(Circuit { }); impl Define for Circuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let sum = builder.add(self.x[0], self.x[1]); let sum = builder.add(sum, 123); builder.assert_is_equal(sum, self.sum); @@ -15,7 +15,7 @@ impl Define for Circuit { #[test] fn test_circuit_eval_simple() { - let compile_result = compile(&Circuit::default()).unwrap(); + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); let assignment = Circuit:: { sum: M31::from(126), x: [M31::from(1), M31::from(2)], diff --git a/expander_compiler/tests/to_binary_hint.rs b/expander_compiler/tests/to_binary_hint.rs index 258a5e00..1ca99cac 100644 --- a/expander_compiler/tests/to_binary_hint.rs +++ b/expander_compiler/tests/to_binary_hint.rs @@ -7,11 +7,11 @@ declare_circuit!(Circuit { input: PublicVariable, }); -fn to_binary(api: &mut API, x: Variable, n_bits: usize) -> Vec { +fn to_binary(api: &mut impl RootAPI, x: Variable, n_bits: usize) -> Vec { api.new_hint("myhint.tobinary", &[x], n_bits) } -fn from_binary(api: &mut API, bits: Vec) -> Variable { +fn from_binary(api: &mut impl RootAPI, bits: Vec) -> Variable { let mut res = api.constant(0); for i in 0..bits.len() { let coef = 1 << i; @@ -22,7 +22,7 @@ fn from_binary(api: &mut API, bits: Vec) -> Variable { } impl Define for Circuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let bits = to_binary(builder, self.input, 8); let x = from_binary(builder, bits); builder.assert_is_equal(x, self.input); @@ -42,7 +42,7 @@ fn test_300() { let mut hint_registry = HintRegistry::::new(); hint_registry.register("myhint.tobinary", to_binary_hint); - let compile_result = compile(&Circuit::default()).unwrap(); + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); for i in 0..300 { let assignment = Circuit:: { input: M31::from(i as u32), @@ -73,7 +73,7 @@ fn test_300_closure() { }, ); - let compile_result = compile(&Circuit::default()).unwrap(); + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); for i in 0..300 { let assignment = Circuit:: { input: M31::from(i as u32), diff --git a/expander_compiler/tests/to_binary_unconstrained_api.rs b/expander_compiler/tests/to_binary_unconstrained_api.rs index bf2712a9..d5ea9567 100644 --- a/expander_compiler/tests/to_binary_unconstrained_api.rs +++ b/expander_compiler/tests/to_binary_unconstrained_api.rs @@ -1,11 +1,10 @@ use expander_compiler::frontend::*; -use extra::UnconstrainedAPI; declare_circuit!(Circuit { input: PublicVariable, }); -fn to_binary(api: &mut API, x: Variable, n_bits: usize) -> Vec { +fn to_binary(api: &mut impl RootAPI, x: Variable, n_bits: usize) -> Vec { let mut res = Vec::new(); for i in 0..n_bits { let y = api.unconstrained_shift_r(x, i as u32); @@ -14,7 +13,7 @@ fn to_binary(api: &mut API, x: Variable, n_bits: usize) -> Vec(api: &mut API, bits: Vec) -> Variable { +fn from_binary(api: &mut impl RootAPI, bits: Vec) -> Variable { let mut res = api.constant(0); for i in 0..bits.len() { let coef = 1 << i; @@ -25,7 +24,7 @@ fn from_binary(api: &mut API, bits: Vec) -> Variable { } impl Define for Circuit { - fn define(&self, builder: &mut API) { + fn define>(&self, builder: &mut Builder) { let bits = to_binary(builder, self.input, 8); let x = from_binary(builder, bits); builder.assert_is_equal(x, self.input); @@ -34,7 +33,7 @@ impl Define for Circuit { #[test] fn test_300() { - let compile_result = compile(&Circuit::default()).unwrap(); + let compile_result = compile(&Circuit::default(), CompileOptions::default()).unwrap(); for i in 0..300 { let assignment = Circuit:: { input: M31::from(i as u32),