From cd39e5564e1656a2ff89b976fa814e59656d87f2 Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:48:49 +0100 Subject: [PATCH] chore: serialize/deserialize processed graph (#371) --- .github/workflows/rust.yml | 130 +++++-------- Cargo.lock | 319 +++++++++++++++++++++++++++----- Cargo.toml | 1 + README.md | 3 +- benches/accum_conv.rs | 18 +- examples/conv2d_mnist/main.rs | 89 ++++----- examples/mlp_4d_einsum.rs | 49 ++--- rust-toolchain | 2 +- src/circuit/modules/poseidon.rs | 8 +- src/circuit/ops/hybrid.rs | 7 +- src/circuit/ops/lookup.rs | 1 + src/circuit/ops/mod.rs | 51 +++-- src/circuit/ops/poly.rs | 82 +++++--- src/circuit/tests.rs | 108 +++++------ src/eth.rs | 4 +- src/execute.rs | 8 +- src/graph/mod.rs | 81 ++++---- src/graph/model.rs | 41 +++- src/graph/modules.rs | 30 +-- src/graph/node.rs | 15 +- src/graph/utilities.rs | 82 +++----- src/graph/vars.rs | 2 +- src/pfsys/evm/aggregation.rs | 8 +- src/tensor/mod.rs | 27 +++ src/tensor/val.rs | 14 +- tests/integration_tests.rs | 31 +++- tests/py_integration_tests.rs | 4 +- tests/wasi_integration_tests.rs | 186 ------------------- tests/wasm/test.key | Bin 928 -> 928 bytes tests/wasm/test.proof | 2 +- tests/wasm/test.provekey | Bin 185440 -> 185440 bytes 31 files changed, 767 insertions(+), 636 deletions(-) delete mode 100644 tests/wasi_integration_tests.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2c9c9cd2..92dacc8a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,31 +26,12 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - name: Build run: cargo build --verbose - build-wasm: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2023-06-27 - override: true - components: rustfmt, clippy - - uses: mwilliamson/setup-wasmtime-action@v2 - with: - wasmtime-version: "3.0.1" - - name: Install wasm32-wasi - run: rustup target add wasm32-wasi - - name: Build wasm - run: cargo build --release --bin ezkl --target=wasm32-wasi - - name: Run help - run: wasmtime run './target/wasm32-wasi/release/ezkl.wasm' -- --help docs: runs-on: ubuntu-latest @@ -58,7 +39,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - name: Docs @@ -70,7 +51,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -83,14 +64,30 @@ jobs: - name: Library tests run: cargo nextest run --lib --verbose -- --include-ignored - wasm32-tests: - runs-on: ubuntu-latest - needs: [build, build-wasm, library-tests, docs] + model-serialization: + runs-on: ubuntu-latest-32-cores steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 + override: true + components: rustfmt, clippy + - uses: baptiste0928/cargo-install@v1 + with: + crate: cargo-nextest + locked: true + - name: Model serialization + run: cargo nextest run native_tests::tests::model_serialization_ + + wasm32-tests: + runs-on: ubuntu-latest + needs: [build, library-tests, docs] + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: jetli/wasm-pack-action@v0.4.0 @@ -104,18 +101,18 @@ jobs: - name: Install wasm runner run: cargo install wasm-server-runner - name: Add rust-src - run: rustup component add rust-src --toolchain nightly-2023-06-27-x86_64-unknown-linux-gnu + run: rustup component add rust-src --toolchain nightly-2023-04-17-x86_64-unknown-linux-gnu - name: Run wasm verifier tests run: wasm-pack test --chrome --headless -- -Z build-std="panic_abort,std" render-circuit: runs-on: ubuntu-latest-32-cores - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: mwilliamson/setup-wasmtime-action@v2 @@ -130,12 +127,12 @@ jobs: tutorial: runs-on: ubuntu-latest - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -147,12 +144,12 @@ jobs: mock-proving-tests: runs-on: ubuntu-latest-32-cores - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -169,6 +166,8 @@ jobs: run: cargo nextest run --release --verbose tests::mock_public_params_ --test-threads 32 - name: Mock proving tests (hashed inputs) run: cargo nextest run --release --verbose tests::mock_hashed_input_::t --test-threads 32 + - name: Mock proving tests (hashed params) + run: cargo nextest run --release --verbose tests::mock_hashed_params_::t --test-threads 32 - name: Mock proving tests (hashed outputs) run: cargo nextest run --release --verbose tests::mock_hashed_output_::t --test-threads 32 - name: Mock proving tests (encrypted inputs) @@ -184,41 +183,13 @@ jobs: - name: Mock proving tests (encrypted inputs + hashed params) run: cargo nextest run --release --verbose tests::mock_encrypted_input_hashed_params_::t --test-threads 32 - mock-proving-tests-wasi: - runs-on: ubuntu-latest-32-cores - needs: [build, build-wasm, library-tests, docs] - steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2023-06-27 - override: true - components: rustfmt, clippy - - uses: mwilliamson/setup-wasmtime-action@v2 - with: - wasmtime-version: "3.0.1" - - uses: baptiste0928/cargo-install@v1 - with: - crate: cargo-nextest - locked: true - - name: Install wasm32-wasi - run: rustup target add wasm32-wasi - - name: Mock proving tests (WASI) (public outputs) - run: cargo nextest run --release --verbose tests_wasi::mock_public_outputs_ --test-threads 32 - - name: Mock proving tests (WASI) (public inputs) - run: cargo nextest run --release --verbose tests_wasi::mock_public_inputs_ --test-threads 32 - - name: Mock proving tests (WASI) (public params) - run: cargo nextest run --release --verbose tests_wasi::mock_public_params_ --test-threads 32 - prove-and-verify-evm-tests: runs-on: ubuntu-latest-16-cores needs: [ build, - build-wasm, library-tests, mock-proving-tests, - mock-proving-tests-wasi, python-tests, python-integration-tests, ] @@ -226,7 +197,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -257,10 +228,8 @@ jobs: needs: [ build, - build-wasm, library-tests, mock-proving-tests, - mock-proving-tests-wasi, python-tests, python-integration-tests, ] @@ -268,7 +237,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -291,10 +260,8 @@ jobs: needs: [ build, - build-wasm, library-tests, mock-proving-tests, - mock-proving-tests-wasi, python-tests, python-integration-tests, ] @@ -302,7 +269,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -323,14 +290,13 @@ jobs: needs: [ build, - build-wasm, library-tests, ] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -345,10 +311,8 @@ jobs: needs: [ build, - build-wasm, library-tests, mock-proving-tests, - mock-proving-tests-wasi, python-tests, python-integration-tests, ] @@ -356,7 +320,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -371,17 +335,15 @@ jobs: needs: [ build, - build-wasm, library-tests, mock-proving-tests, - mock-proving-tests-wasi, python-tests, ] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -400,12 +362,12 @@ jobs: examples: runs-on: ubuntu-latest-32-cores - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -419,12 +381,12 @@ jobs: neg-tests: runs-on: ubuntu-latest - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 @@ -436,7 +398,7 @@ jobs: python-tests: runs-on: ubuntu-latest-32-cores - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 @@ -444,7 +406,7 @@ jobs: python-version: "3.7" - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - name: Install solc @@ -460,7 +422,7 @@ jobs: python-integration-tests: runs-on: 512gb - needs: [build, build-wasm, library-tests, docs] + needs: [build, library-tests, docs] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 @@ -468,7 +430,7 @@ jobs: python-version: "3.9" - uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2023-06-27 + toolchain: nightly-2023-04-17 override: true components: rustfmt, clippy - uses: baptiste0928/cargo-install@v1 diff --git a/Cargo.lock b/Cargo.lock index 613ba5d5..5d3660b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", ] @@ -46,7 +46,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", "version_check", ] @@ -90,7 +90,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83" dependencies = [ - "nom", + "nom 7.1.3", "vte", ] @@ -189,6 +189,18 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.7", + "stable_deref_trait", +] + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -349,7 +361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ "block-padding", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -358,7 +370,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -376,6 +388,19 @@ dependencies = [ "sha2 0.9.9", ] +[[package]] +name = "build_id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6deb6795d8b4d2269c3fcf87a87bff9f4cd45a99e259806603ee8007077daf3" +dependencies = [ + "byteorder", + "once_cell", + "palaver", + "twox-hash", + "uuid", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -459,6 +484,12 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -598,7 +629,7 @@ dependencies = [ "bech32", "bs58", "digest 0.10.7", - "generic-array", + "generic-array 0.14.7", "hex", "ripemd", "serde", @@ -662,7 +693,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen", ] @@ -757,7 +788,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -802,7 +833,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -812,7 +843,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] @@ -824,7 +855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", "memoffset 0.9.0", "scopeguard", @@ -836,7 +867,7 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -851,7 +882,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -863,7 +894,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -1003,7 +1034,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1033,7 +1064,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1148,7 +1179,7 @@ dependencies = [ "crypto-bigint", "digest 0.10.7", "ff", - "generic-array", + "generic-array 0.14.7", "group", "pkcs8", "rand_core 0.6.4", @@ -1178,7 +1209,7 @@ version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1225,6 +1256,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +[[package]] +name = "erased-serde" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6984864d65d092d9e9ada107007a846a09f75d2e24046bcce9a38d14aa52052" +dependencies = [ + "serde", +] + [[package]] name = "errno" version = "0.3.1" @@ -1413,7 +1453,7 @@ dependencies = [ "chrono", "elliptic-curve", "ethabi", - "generic-array", + "generic-array 0.14.7", "hex", "k256", "num_enum", @@ -1534,7 +1574,7 @@ version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a81c89f121595cf8959e746045bb8b25a6a38d72588561e1a3b7992fc213f674" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dunce", "ethers-core", "glob", @@ -1607,6 +1647,7 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_json", + "serde_traitobject", "shellexpand", "snark-verifier", "tabled", @@ -1667,7 +1708,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", "windows-sys 0.48.0", @@ -1923,6 +1964,24 @@ dependencies = [ "tempfile", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1940,7 +1999,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2086,6 +2145,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -2125,6 +2193,18 @@ dependencies = [ "fxhash", ] +[[package]] +name = "heapless" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +dependencies = [ + "as-slice", + "generic-array 0.13.3", + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -2400,7 +2480,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -2409,7 +2489,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2493,7 +2573,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "ecdsa", "elliptic-curve", "once_cell", @@ -2569,7 +2649,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-sys 0.48.0", ] @@ -2658,6 +2738,15 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "maingate" version = "0.1.0" @@ -2729,6 +2818,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metatype" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23decce7c32638bcefbd5a5a5d79a5bb5b720c47b82ad5cb670a7eb912705946" + [[package]] name = "mime" version = "0.3.17" @@ -2808,6 +2903,25 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nom" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" + [[package]] name = "nom" version = "7.1.3" @@ -2983,7 +3097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -3036,6 +3150,23 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "palaver" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49dfc200733ac34dcd9a1e4a7e454b521723936010bef3710e2d8024a32d685f" +dependencies = [ + "bitflags", + "heapless", + "lazy_static", + "libc", + "mach", + "nix", + "procinfo", + "typenum", + "winapi", +] + [[package]] name = "papergrid" version = "0.9.1" @@ -3091,7 +3222,7 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.3.5", "smallvec", @@ -3494,6 +3625,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procinfo" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab1427f3d2635891f842892dda177883dca0639e05fe66796a62c9d2f23b49c" +dependencies = [ + "byteorder", + "libc", + "nom 2.2.1", + "rustc_version 0.2.3", +] + [[package]] name = "prost" version = "0.11.9" @@ -3523,7 +3666,7 @@ version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b1ac5b3731ba34fdaa9785f8d74d17448cd18f30cf19e0c7e7b1fdb5272109" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "indoc", "libc", "memoffset 0.8.0", @@ -3791,6 +3934,17 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "relative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3401c189ee92c7028ba4863f3fdb92af815789993221af2fa186eed8115da304" +dependencies = [ + "build_id", + "serde", + "uuid", +] + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -3965,6 +4119,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.3.3" @@ -4048,7 +4211,7 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad560913365790f17cbf12479491169f01b9d46d29cfc7422bf8c64bdc61b731" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "derive_more", "parity-scale-codec", "scale-info-derive", @@ -4116,7 +4279,7 @@ checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -4163,13 +4326,22 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.2", ] [[package]] @@ -4181,6 +4353,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.2" @@ -4238,6 +4416,28 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_closure" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9659437bcfbf4dd061a5e1f7994312990ac5b24d781f7ce577eefc3a27792da0" +dependencies = [ + "rustversion", + "serde", + "serde_closure_derive", +] + +[[package]] +name = "serde_closure_derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a6bb4d612b5caad466a9a09ee550445e34123a74075607cc0d882ff1ca28f46" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "serde_derive" version = "1.0.164" @@ -4269,6 +4469,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_traitobject" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5ae15a5d31f7c57875a480ddd7be02314d264617d0294d961314a6d502e6b1" +dependencies = [ + "erased-serde", + "metatype", + "relative", + "serde", + "serde_closure", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4288,7 +4501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -4300,7 +4513,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -4440,6 +4653,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -4458,7 +4677,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "hashbrown 0.11.2", "serde", ] @@ -4628,7 +4847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", "rustix", @@ -4661,7 +4880,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -4867,7 +5086,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4937,7 +5156,7 @@ dependencies = [ "lazy_static", "maplit", "ndarray", - "nom", + "nom 7.1.3", "num-integer", "num-traits", "scan_fmt", @@ -4986,7 +5205,7 @@ dependencies = [ "byteorder", "flate2", "log", - "nom", + "nom 7.1.3", "tar", "tract-core", "walkdir", @@ -5044,6 +5263,16 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "static_assertions", +] + [[package]] name = "typenum" version = "1.16.0" @@ -5152,6 +5381,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "vte" version = "0.10.1" @@ -5210,7 +5445,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "serde", "serde_json", "wasm-bindgen-macro", @@ -5237,7 +5472,7 @@ version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index 00abd231..8384252f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ tokio = { version = "1.26.0", default_features = false, features = ["macros", " rayon = { version = "1.7.0", default_features = false } bincode = { version = "1.3.3", default_features = false } ark-std = { version = "^0.3.0", default-features = false } +serde_traitobject = { version = "0.2.8", features = ["serde_closure"] } # python binding related deps pyo3 = { version = "0.18.3", features = ["extension-module", "abi3-py37", "macros"], default_features = false, optional = true } diff --git a/README.md b/README.md index 8b579e48..c51b59e1 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ https://user-images.githubusercontent.com/45801863/236771676-5bbbbfd1-ba6f-418a- Note that the library requires a nightly version of the rust toolchain. You can change the default toolchain by running: ```bash -rustup override set nightly +# we set it to this version because of https://github.com/rust-lang/rust/issues/110829 +rustup override set nightly-2023-04-17 ``` After which you may build the library diff --git a/benches/accum_conv.rs b/benches/accum_conv.rs index 89ecace1..a0a30881 100644 --- a/benches/accum_conv.rs +++ b/benches/accum_conv.rs @@ -28,8 +28,8 @@ const K: usize = 17; #[derive(Clone, Debug)] struct MyCircuit { image: ValTensor, - kernel: ValTensor, - bias: ValTensor, + kernel: Tensor, + bias: Tensor, } impl Circuit for MyCircuit { @@ -100,18 +100,20 @@ fn runcnvrl(c: &mut Criterion) { .map(|_| Value::known(Fr::random(OsRng))), ); image.reshape(&[1, IN_CHANNELS, IMAGE_HEIGHT, IMAGE_WIDTH]); - let mut kernels = Tensor::from( + let mut kernel = Tensor::from( (0..{ OUT_CHANNELS * IN_CHANNELS * KERNEL_HEIGHT * KERNEL_WIDTH }) - .map(|_| Value::known(Fr::random(OsRng))), + .map(|_| Fr::random(OsRng)), ); - kernels.reshape(&[OUT_CHANNELS, IN_CHANNELS, KERNEL_HEIGHT, KERNEL_WIDTH]); + kernel.reshape(&[OUT_CHANNELS, IN_CHANNELS, KERNEL_HEIGHT, KERNEL_WIDTH]); + kernel.set_visibility(ezkl::graph::Visibility::Private); - let bias = Tensor::from((0..{ OUT_CHANNELS }).map(|_| Value::known(Fr::random(OsRng)))); + let mut bias = Tensor::from((0..{ OUT_CHANNELS }).map(|_| Fr::random(OsRng))); + bias.set_visibility(ezkl::graph::Visibility::Private); let circuit = MyCircuit { image: ValTensor::from(image), - kernel: ValTensor::from(kernels), - bias: ValTensor::from(bias), + kernel, + bias, }; group.throughput(Throughput::Elements(*size as u64)); diff --git a/examples/conv2d_mnist/main.rs b/examples/conv2d_mnist/main.rs index ce66083f..db1505d2 100644 --- a/examples/conv2d_mnist/main.rs +++ b/examples/conv2d_mnist/main.rs @@ -6,6 +6,7 @@ use ezkl::fieldutils; use ezkl::fieldutils::i32_to_felt; use ezkl::tensor::*; use halo2_proofs::dev::MockProver; +use halo2_proofs::poly::kzg::multiopen::VerifierGWC; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{ @@ -14,20 +15,19 @@ use halo2_proofs::{ }, poly::{ commitment::ParamsProver, - ipa::{ - commitment::{IPACommitmentScheme, ParamsIPA}, - multiopen::ProverIPA, + kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::ProverGWC, strategy::SingleStrategy, }, - VerificationStrategy, }, transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }, }; -use halo2curves::ff::PrimeField; -use halo2curves::pasta::vesta; -use halo2curves::pasta::Fp as F; +use halo2curves::bn256::Bn256; +use halo2curves::bn256::Fr as F; + use instant::Instant; use mnist::*; use rand::rngs::OsRng; @@ -39,7 +39,6 @@ const K: usize = 20; #[derive(Clone)] struct Config< - F: PrimeField + TensorType + PartialOrd, const LEN: usize, //LEN = CHOUT x OH x OW flattened //not supported yet in rust stable const CLASSES: usize, const BITS: usize, @@ -63,7 +62,6 @@ struct Config< #[derive(Clone)] struct MyCircuit< - F: PrimeField + TensorType + PartialOrd, const LEN: usize, //LEN = CHOUT x OH x OW flattened const CLASSES: usize, const BITS: usize, @@ -82,12 +80,11 @@ struct MyCircuit< // Given the stateless ConvConfig type information, a DNN trace is determined by its input and the parameters of its layers. // Computing the trace still requires a forward pass. The intermediate activations are stored only by the layouter. input: ValTensor, - l0_params: [ValTensor; 2], - l2_params: [ValTensor; 2], + l0_params: [Tensor; 2], + l2_params: [Tensor; 2], } impl< - F: PrimeField + TensorType + PartialOrd, const LEN: usize, const CLASSES: usize, const BITS: usize, @@ -102,7 +99,6 @@ impl< const PADDING: usize, > Circuit for MyCircuit< - F, LEN, CLASSES, BITS, @@ -119,7 +115,6 @@ where Value: TensorType, { type Config = Config< - F, LEN, CLASSES, BITS, @@ -203,7 +198,7 @@ where .layer_config .layout( &mut region, - &[self.l2_params[0].clone(), x], + &[self.l2_params[0].clone().into(), x], Box::new(PolyOp::Einsum { equation: "ij,j->ik".to_string(), }), @@ -295,7 +290,7 @@ pub fn runconv() { input.reshape(&[1, 1, 28, 28]).unwrap(); let myparams = params::Params::new(); - let mut l0_kernels: ValTensor = Tensor::>::from( + let mut l0_kernels = Tensor::::from( myparams .kernels .clone() @@ -307,47 +302,35 @@ pub fn runconv() { let dx = fl * 32_f32; let rounded = dx.round(); let integral: i32 = unsafe { rounded.to_int_unchecked() }; - let felt = fieldutils::i32_to_felt(integral); - Value::known(felt) + fieldutils::i32_to_felt(integral) }), - ) - .into(); + ); - l0_kernels - .reshape(&[OUT_CHANNELS, IN_CHANNELS, KERNEL_HEIGHT, KERNEL_WIDTH]) - .unwrap(); + l0_kernels.reshape(&[OUT_CHANNELS, IN_CHANNELS, KERNEL_HEIGHT, KERNEL_WIDTH]); + l0_kernels.set_visibility(ezkl::graph::Visibility::Private); - let l0_bias: ValTensor = Tensor::>::from( - (0..OUT_CHANNELS).map(|_| Value::known(fieldutils::i32_to_felt(0))), - ) - .into(); + let mut l0_bias = Tensor::::from((0..OUT_CHANNELS).map(|_| fieldutils::i32_to_felt(0))); + l0_bias.set_visibility(ezkl::graph::Visibility::Private); - let mut l2_biases: ValTensor = - Tensor::>::from(myparams.biases.into_iter().map(|fl| { - let dx = fl * 32_f32; - let rounded = dx.round(); - let integral: i32 = unsafe { rounded.to_int_unchecked() }; - let felt = fieldutils::i32_to_felt(integral); - Value::known(felt) - })) - .into(); + let mut l2_biases = Tensor::::from(myparams.biases.into_iter().map(|fl| { + let dx = fl * 32_f32; + let rounded = dx.round(); + let integral: i32 = unsafe { rounded.to_int_unchecked() }; + fieldutils::i32_to_felt(integral) + })); + l2_biases.set_visibility(ezkl::graph::Visibility::Private); + l2_biases.reshape(&[l2_biases.len(), 1]); - l2_biases.reshape(&[l2_biases.len(), 1]).unwrap(); - - let mut l2_weights: ValTensor = - Tensor::>::from(myparams.weights.into_iter().flatten().map(|fl| { - let dx = fl * 32_f32; - let rounded = dx.round(); - let integral: i32 = unsafe { rounded.to_int_unchecked() }; - let felt = fieldutils::i32_to_felt(integral); - Value::known(felt) - })) - .into(); - - l2_weights.reshape(&[CLASSES, LEN]).unwrap(); + let mut l2_weights = Tensor::::from(myparams.weights.into_iter().flatten().map(|fl| { + let dx = fl * 32_f32; + let rounded = dx.round(); + let integral: i32 = unsafe { rounded.to_int_unchecked() }; + fieldutils::i32_to_felt(integral) + })); + l2_weights.set_visibility(ezkl::graph::Visibility::Private); + l2_weights.reshape(&[CLASSES, LEN]); let circuit = MyCircuit::< - F, LEN, 10, 16, @@ -411,7 +394,7 @@ pub fn runconv() { // Real proof println!("SRS GENERATION"); let now = Instant::now(); - let params: ParamsIPA = ParamsIPA::new(K as u32); + let params: ParamsKZG = ParamsKZG::new(K as u32); let elapsed = now.elapsed(); println!( "SRS GENERATION took {}.{}", @@ -446,7 +429,7 @@ pub fn runconv() { let now = Instant::now(); let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let mut rng = OsRng; - create_proof::, ProverIPA<_>, _, _, _, _>( + create_proof::, ProverGWC<_>, _, _, _, _>( ¶ms, &pk, &[circuit], @@ -466,7 +449,7 @@ pub fn runconv() { let now = Instant::now(); let strategy = SingleStrategy::new(¶ms); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); - let verify = verify_proof( + let verify = verify_proof::<_, VerifierGWC<_>, _, _, _>( ¶ms, pk.get_vk(), strategy, diff --git a/examples/mlp_4d_einsum.rs b/examples/mlp_4d_einsum.rs index 815b52c5..dbe63dbe 100644 --- a/examples/mlp_4d_einsum.rs +++ b/examples/mlp_4d_einsum.rs @@ -9,36 +9,32 @@ use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{Circuit, Column, ConstraintSystem, Error, Instance}, }; -use halo2curves::ff::PrimeField; -use halo2curves::pasta::Fp as F; +use halo2curves::bn256::Fr as F; use std::marker::PhantomData; const K: usize = 15; // A columnar ReLu MLP #[derive(Clone)] -struct MyConfig { +struct MyConfig { layer_config: PolyConfig, public_output: Column, } #[derive(Clone)] struct MyCircuit< - F: PrimeField + TensorType + PartialOrd, const LEN: usize, //LEN = CHOUT x OH x OW flattened const BITS: usize, > { // Given the stateless MyConfig type information, a DNN trace is determined by its input and the parameters of its layers. // Computing the trace still requires a forward pass. The intermediate activations are stored only by the layouter. input: ValTensor, - l0_params: [ValTensor; 2], - l2_params: [ValTensor; 2], + l0_params: [Tensor; 2], + l2_params: [Tensor; 2], _marker: PhantomData, } -impl Circuit - for MyCircuit -{ - type Config = MyConfig; +impl Circuit for MyCircuit { + type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; type Params = PhantomData; @@ -100,7 +96,7 @@ implac".to_string(), }), @@ -138,7 +134,7 @@ implac".to_string(), }), @@ -202,34 +198,39 @@ impl> = Tensor::::new( + let mut l0_kernel: Tensor = Tensor::::new( Some(&[10, 0, 0, -1, 0, 10, 1, 0, 0, 1, 10, 0, 1, 0, 0, 10]), &[4, 4], ) .unwrap() - .into(); - let l0_bias: Tensor> = Tensor::::new(Some(&[0, 0, 0, 1]), &[4, 1]) - .unwrap() - .into(); + .map(i32_to_felt); + l0_kernel.set_visibility(ezkl::graph::Visibility::Private); - let l2_kernel: Tensor> = Tensor::::new( + let mut l0_bias: Tensor = Tensor::::new(Some(&[0, 0, 0, 1]), &[4, 1]) + .unwrap() + .map(i32_to_felt); + l0_bias.set_visibility(ezkl::graph::Visibility::Private); + + let mut l2_kernel: Tensor = Tensor::::new( Some(&[0, 3, 10, -1, 0, 10, 1, 0, 0, 1, 0, 12, 1, -2, 32, 0]), &[4, 4], ) .unwrap() - .into(); + .map(i32_to_felt); + l2_kernel.set_visibility(ezkl::graph::Visibility::Private); // input data, with 1 padding to allow for bias let input: Tensor> = Tensor::::new(Some(&[-30, -21, 11, 40]), &[4, 1]) .unwrap() .into(); - let l2_bias: Tensor> = Tensor::::new(Some(&[0, 0, 0, 1]), &[4, 1]) + let mut l2_bias: Tensor = Tensor::::new(Some(&[0, 0, 0, 1]), &[4, 1]) .unwrap() - .into(); + .map(i32_to_felt); + l2_bias.set_visibility(ezkl::graph::Visibility::Private); - let circuit = MyCircuit:: { + let circuit = MyCircuit::<4, 14> { input: input.into(), - l0_params: [l0_kernel.into(), l0_bias.into()], - l2_params: [l2_kernel.into(), l2_bias.into()], + l0_params: [l0_kernel, l0_bias], + l2_params: [l2_kernel, l2_bias], _marker: PhantomData, }; diff --git a/rust-toolchain b/rust-toolchain index 82e33778..014bb1ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-06-27" +channel = "nightly-2023-04-16" components = [ "rustfmt", "clippy" ] diff --git a/src/circuit/modules/poseidon.rs b/src/circuit/modules/poseidon.rs index 8ee47bb8..d68cd9a5 100644 --- a/src/circuit/modules/poseidon.rs +++ b/src/circuit/modules/poseidon.rs @@ -69,8 +69,8 @@ impl + Sync, const WIDTH: usize, const RATE: usize, con meta, hash_inputs.clone().try_into().unwrap(), partial_sbox, - rc_a.try_into().unwrap(), - rc_b.try_into().unwrap(), + rc_a, + rc_b, ); PoseidonConfig { @@ -290,9 +290,9 @@ impl + Sync, const WIDTH: usize, const RATE: usize, con }, )?; - return Ok(assigned_input.into()); + Ok(assigned_input.into()) } else { - return Ok(result.into()); + Ok(result.into()) } } diff --git a/src/circuit/ops/hybrid.rs b/src/circuit/ops/hybrid.rs index 87604d3c..ced627ca 100644 --- a/src/circuit/ops/hybrid.rs +++ b/src/circuit/ops/hybrid.rs @@ -1,5 +1,4 @@ -use std::any::Any; - +use super::*; use crate::{ circuit::{self, layouts, Tolerance}, fieldutils::{felt_to_i128, i128_to_felt}, @@ -7,8 +6,6 @@ use crate::{ tensor::{self, Tensor, TensorError, TensorType, ValTensor}, }; use serde::{Deserialize, Serialize}; - -use super::{lookup::LookupOp, region::RegionCtx, ForwardResult, Op}; use halo2curves::ff::PrimeField; // import run args from model @@ -34,10 +31,10 @@ pub enum HybridOp { } impl Op for HybridOp { + /// Returns a reference to the Any trait. fn as_any(&self) -> &dyn Any { self } - /// Matches a [Op] to an operation in the `tensor::ops` module. fn f(&self, inputs: &[Tensor]) -> Result, TensorError> { let x = inputs[0].clone().map(|x| felt_to_i128(x)); diff --git a/src/circuit/ops/lookup.rs b/src/circuit/ops/lookup.rs index 5d725309..0b4b8d43 100644 --- a/src/circuit/ops/lookup.rs +++ b/src/circuit/ops/lookup.rs @@ -41,6 +41,7 @@ impl LookupOp { } impl Op for LookupOp { + /// Returns a reference to the Any trait. fn as_any(&self) -> &dyn Any { self } diff --git a/src/circuit/ops/mod.rs b/src/circuit/ops/mod.rs index f586a9e4..426c5d37 100644 --- a/src/circuit/ops/mod.rs +++ b/src/circuit/ops/mod.rs @@ -34,7 +34,9 @@ pub struct ForwardResult { } /// An enum representing operations that can be represented as constraints in a circuit. -pub trait Op: std::fmt::Debug + Send + Sync + Any { +pub trait Op: + std::fmt::Debug + Send + Sync + Any + serde_traitobject::Serialize + serde_traitobject::Deserialize +{ /// Matches a [Op] to an operation in the `tensor::ops` module. fn f(&self, x: &[Tensor]) -> Result, TensorError>; /// Returns a string representation of the operation. @@ -134,15 +136,16 @@ impl Op for Input { } /// A wrapper for an operation that has been rescaled. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Rescaled { /// The operation to be rescaled. + #[serde(with = "serde_traitobject")] pub inner: Box>, /// The scale of the operation's inputs. pub scale: Vec<(usize, u128)>, } -impl Op for Rescaled { +impl Op for Rescaled { fn as_any(&self) -> &dyn Any { self } @@ -214,7 +217,7 @@ impl Op for Rescaled { } /// An unknown operation. -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct Unknown; impl Op for Unknown { @@ -246,32 +249,41 @@ impl Op for Unknown { } /// -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Constant { /// - pub quantized_values: ValTensor, + pub quantized_values: Tensor, /// pub raw_values: Tensor, + /// + #[serde(skip)] + pub pre_assigned_val: Option>, } impl Constant { /// - pub fn new(quantized_values: ValTensor, raw_values: Tensor) -> Self { + pub fn new(quantized_values: Tensor, raw_values: Tensor) -> Self { Self { quantized_values, raw_values, + pre_assigned_val: None, } } + + /// + pub fn pre_assign(&mut self, val: ValTensor) { + self.pre_assigned_val = Some(val) + } } -impl Op for Constant { +impl Deserialize<'de>> Op + for Constant +{ fn as_any(&self) -> &dyn Any { self } fn f(&self, _: &[Tensor]) -> Result, TensorError> { - let mut output = self.quantized_values.get_felt_evals().unwrap(); - // make sure its the right shape - output.reshape(self.quantized_values.dims()); + let output = self.quantized_values.clone(); Ok(ForwardResult { output, @@ -284,11 +296,20 @@ impl Op for Constant { } fn layout( &self, - _: &mut crate::circuit::BaseConfig, - _: &mut RegionCtx, + config: &mut crate::circuit::BaseConfig, + region: &mut RegionCtx, _: &[ValTensor], ) -> Result>, Box> { - Ok(Some(self.quantized_values.clone())) + if let Some(value) = &self.pre_assigned_val { + Ok(Some(value.clone())) + } else { + // we gotta constrain it once + Ok(Some(layouts::identity( + config, + region, + &[self.quantized_values.clone().into()], + )?)) + } } fn rescale(&self, _: Vec, _: u32) -> Box> { Box::new(self.clone()) @@ -299,7 +320,7 @@ impl Op for Constant { } } -fn homogenize_input_scales( +fn homogenize_input_scales( op: impl Op + Clone, input_scales: Vec, inputs_to_scale: Vec, diff --git a/src/circuit/ops/poly.rs b/src/circuit/ops/poly.rs index 82e90ca6..702972b1 100644 --- a/src/circuit/ops/poly.rs +++ b/src/circuit/ops/poly.rs @@ -7,14 +7,14 @@ use super::{base::BaseOp, *}; #[allow(missing_docs)] /// An enum representing the operations that can be expressed as arithmetic (non lookup) operations. -#[derive(Clone, Debug)] -pub enum PolyOp { +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum PolyOp{ Einsum { equation: String, }, Conv { - kernel: ValTensor, - bias: Option>, + kernel: Tensor, + bias: Option>, padding: (usize, usize), stride: (usize, usize), }, @@ -24,8 +24,8 @@ pub enum PolyOp { modulo: usize, }, DeConv { - kernel: ValTensor, - bias: Option>, + kernel: Tensor, + bias: Option>, padding: (usize, usize), output_padding: (usize, usize), stride: (usize, usize), @@ -36,11 +36,11 @@ pub enum PolyOp { kernel_shape: (usize, usize), }, Add { - a: Option>, + a: Option>, }, Sub, Mult { - a: Option>, + a: Option>, }, Identity, Reshape(Vec), @@ -76,10 +76,14 @@ pub enum PolyOp { impl PolyOp {} -impl Op for PolyOp { +impl Deserialize<'de>> Op for PolyOp + +{ + /// Returns a reference to the Any trait. fn as_any(&self) -> &dyn Any { self } + fn as_string(&self) -> String { let name = match &self { PolyOp::MoveAxis { .. } => "MOVEAXIS", @@ -144,14 +148,14 @@ impl Op for PolyOp { } PolyOp::Add { a } => { if let Some(a) = a { - inputs.push(Tensor::new(Some(&a.get_felt_evals().unwrap()), a.dims())?); + inputs.push(a.clone()); } tensor::ops::add(&inputs) } PolyOp::Sub => tensor::ops::sub(&inputs), PolyOp::Mult { a } => { if let Some(a) = a { - inputs.push(Tensor::new(Some(&a.get_felt_evals().unwrap()), a.dims())?); + inputs.push(a.clone()); } tensor::ops::mult(&inputs) } @@ -161,9 +165,9 @@ impl Op for PolyOp { padding, stride, } => { - inputs.push(Tensor::new(Some(&a.get_felt_evals().unwrap()), a.dims())?); + inputs.push(a.clone()); if let Some(b) = bias { - inputs.push(Tensor::new(Some(&b.get_felt_evals().unwrap()), b.dims())?); + inputs.push(b.clone()); } tensor::ops::conv(&inputs, *padding, *stride) } @@ -174,9 +178,9 @@ impl Op for PolyOp { output_padding, stride, } => { - inputs.push(Tensor::new(Some(&a.get_felt_evals().unwrap()), a.dims())?); + inputs.push(a.clone()); if let Some(b) = bias { - inputs.push(Tensor::new(Some(&b.get_felt_evals().unwrap()), b.dims())?); + inputs.push(b.clone()); } tensor::ops::deconv(&inputs, *padding, *output_padding, *stride) } @@ -260,9 +264,9 @@ impl Op for PolyOp { padding, stride, } => { - values.push(kernel.clone()); + values.push(kernel.clone().into()); if let Some(bias) = bias { - values.push(bias.clone()); + values.push(bias.clone().into()); } layouts::conv(config, region, values[..].try_into()?, *padding, *stride)? } @@ -273,9 +277,9 @@ impl Op for PolyOp { output_padding, stride, } => { - values.push(kernel.clone()); + values.push(kernel.clone().into()); if let Some(bias) = bias { - values.push(bias.clone()); + values.push(bias.clone().into()); } layouts::deconv( config, @@ -300,7 +304,7 @@ impl Op for PolyOp { )?, PolyOp::Add { a } => { if let Some(a) = a { - values.push(a.clone()); + values.push(a.clone().into()); } layouts::pairwise(config, region, values[..].try_into()?, BaseOp::Add)? @@ -308,7 +312,7 @@ impl Op for PolyOp { PolyOp::Sub => layouts::pairwise(config, region, values[..].try_into()?, BaseOp::Sub)?, PolyOp::Mult { a } => { if let Some(a) = a { - values.push(a.clone()); + values.push(a.clone().into()); } layouts::pairwise(config, region, values[..].try_into()?, BaseOp::Mult)? } @@ -356,16 +360,32 @@ impl Op for PolyOp { PolyOp::Sum { .. } => in_scales[0], PolyOp::Conv { kernel, bias, .. } => { - let output_scale = in_scales[0] + kernel.scale(); + let kernel_scale = match kernel.scale() { + Some(s) => s, + None => panic!("scale must be set for conv kernel"), + }; + let output_scale = in_scales[0] + kernel_scale; if let Some(b) = bias { - assert_eq!(output_scale, b.scale()); + let bias_scale = match b.scale() { + Some(s) => s, + None => panic!("scale must be set for conv bias"), + }; + assert_eq!(output_scale, bias_scale); } output_scale } PolyOp::DeConv { kernel, bias, .. } => { - let output_scale = in_scales[0] + kernel.scale(); + let kernel_scale = match kernel.scale() { + Some(s) => s, + None => panic!("scale must be set for deconv kernel"), + }; + let output_scale = in_scales[0] + kernel_scale; if let Some(b) = bias { - assert_eq!(output_scale, b.scale()); + let bias_scale = match b.scale() { + Some(s) => s, + None => panic!("scale must be set for deconv bias"), + }; + assert_eq!(output_scale, bias_scale); } output_scale } @@ -374,7 +394,11 @@ impl Op for PolyOp { let mut scale_a = 0; let scale_b = in_scales[0]; if let Some(a) = a { - scale_a += a.scale(); + let a_scale = match a.scale() { + Some(s) => s, + None => panic!("scale must be set for add constant"), + }; + scale_a += a_scale; } else { scale_a += in_scales[1]; } @@ -385,7 +409,11 @@ impl Op for PolyOp { PolyOp::Mult { a } => { let mut scale = in_scales[0]; if let Some(a) = a { - scale += a.scale(); + let a_scale = match a.scale() { + Some(s) => s, + None => panic!("scale must be set for add constant"), + }; + scale += a_scale; } else { scale += in_scales[1]; } diff --git a/src/circuit/tests.rs b/src/circuit/tests.rs index 2d6a8b12..4d92024c 100644 --- a/src/circuit/tests.rs +++ b/src/circuit/tests.rs @@ -7,13 +7,13 @@ use halo2_proofs::{ dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; +use halo2curves::bn256::Fr as F; use halo2curves::ff::{Field, PrimeField}; -use halo2curves::pasta::pallas; -use halo2curves::pasta::Fp as F; use ops::lookup::LookupOp; use ops::region::RegionCtx; use rand::rngs::OsRng; use std::marker::PhantomData; + #[derive(Default)] struct TestParams; @@ -31,7 +31,7 @@ mod matmul { _marker: PhantomData, } - impl Circuit for MatmulCircuit { + impl Circuit for MatmulCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -107,7 +107,7 @@ mod matmul_col_overflow { _marker: PhantomData, } - impl Circuit for MatmulCircuit { + impl Circuit for MatmulCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -183,7 +183,7 @@ mod dot { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -256,7 +256,7 @@ mod dot_col_overflow { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -329,7 +329,7 @@ mod sum { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -398,7 +398,7 @@ mod sum_col_overflow { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -468,7 +468,7 @@ mod composition { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -557,11 +557,11 @@ mod conv { #[derive(Clone)] struct ConvCircuit { - inputs: Vec>, + inputs: Vec>, _marker: PhantomData, } - impl Circuit for ConvCircuit { + impl Circuit for ConvCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -590,7 +590,7 @@ mod conv { config .layout( &mut region, - &[self.inputs[0].clone()], + &[self.inputs[0].clone().into()], Box::new(PolyOp::Conv { kernel: self.inputs[1].clone(), bias: None, @@ -616,27 +616,23 @@ mod conv { let in_channels = 3; let out_channels = 2; - let mut image = Tensor::from( - (0..in_channels * image_height * image_width) - .map(|_| Value::known(pallas::Base::random(OsRng))), - ); + let mut image = + Tensor::from((0..in_channels * image_height * image_width).map(|_| F::random(OsRng))); image.reshape(&[1, in_channels, image_height, image_width]); + image.set_visibility(crate::graph::Visibility::Private); + let mut kernels = Tensor::from( (0..{ out_channels * in_channels * kernel_height * kernel_width }) - .map(|_| Value::known(pallas::Base::random(OsRng))), + .map(|_| F::random(OsRng)), ); kernels.reshape(&[out_channels, in_channels, kernel_height, kernel_width]); + kernels.set_visibility(crate::graph::Visibility::Private); - let bias = - Tensor::from((0..{ out_channels }).map(|_| Value::known(pallas::Base::random(OsRng)))); + let mut bias = Tensor::from((0..{ out_channels }).map(|_| F::random(OsRng))); + bias.set_visibility(crate::graph::Visibility::Private); let circuit = ConvCircuit:: { - inputs: [ - ValTensor::from(image), - ValTensor::from(kernels), - ValTensor::from(bias), - ] - .to_vec(), + inputs: [image, kernels, bias].to_vec(), _marker: PhantomData, }; @@ -654,18 +650,20 @@ mod conv { let in_channels = 3; let out_channels = 2; - let mut image = Tensor::from( - (0..in_channels * image_height * image_width).map(|i| Value::known(F::from(i as u64))), - ); + let mut image = + Tensor::from((0..in_channels * image_height * image_width).map(|i| F::from(i as u64))); image.reshape(&[1, in_channels, image_height, image_width]); + image.set_visibility(crate::graph::Visibility::Private); + let mut kernels = Tensor::from( (0..{ out_channels * in_channels * kernel_height * kernel_width }) - .map(|i| Value::known(F::from(i as u64))), + .map(|i| F::from(i as u64)), ); kernels.reshape(&[out_channels, in_channels, kernel_height, kernel_width]); + kernels.set_visibility(crate::graph::Visibility::Private); let circuit = ConvCircuit:: { - inputs: [ValTensor::from(image), ValTensor::from(kernels)].to_vec(), + inputs: [image, kernels].to_vec(), _marker: PhantomData, }; @@ -688,7 +686,7 @@ mod sumpool { _marker: PhantomData, } - impl Circuit for ConvCircuit { + impl Circuit for ConvCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -739,8 +737,7 @@ mod sumpool { let in_channels = 1; let mut image = Tensor::from( - (0..in_channels * image_height * image_width) - .map(|_| Value::known(pallas::Base::random(OsRng))), + (0..in_channels * image_height * image_width).map(|_| Value::known(F::random(OsRng))), ); image.reshape(&[1, in_channels, image_height, image_width]); @@ -767,7 +764,7 @@ mod add_w_shape_casting { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -838,7 +835,7 @@ mod add { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -909,7 +906,7 @@ mod add_with_overflow { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1118,7 +1115,7 @@ mod sub { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1185,7 +1182,7 @@ mod mult { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1256,7 +1253,7 @@ mod pow { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1321,7 +1318,7 @@ mod pack { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1390,7 +1387,7 @@ mod rescaled { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1486,7 +1483,7 @@ mod matmul_relu { base_config: BaseConfig, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1572,7 +1569,6 @@ mod rangecheckpercent { dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; - use halo2curves::pasta::Fp; const RANGE: f32 = 1.0; // 1 percent error tolerance const K: usize = 18; @@ -1588,7 +1584,7 @@ mod rangecheckpercent { _marker: PhantomData, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1647,9 +1643,9 @@ mod rangecheckpercent { fn test_range_check_percent() { // Successful cases { - let inp = Tensor::new(Some(&[Value::::known(Fp::from(100_u64))]), &[1]).unwrap(); - let out = Tensor::new(Some(&[Value::::known(Fp::from(101_u64))]), &[1]).unwrap(); - let circuit = MyCircuit:: { + let inp = Tensor::new(Some(&[Value::::known(F::from(100_u64))]), &[1]).unwrap(); + let out = Tensor::new(Some(&[Value::::known(F::from(101_u64))]), &[1]).unwrap(); + let circuit = MyCircuit:: { input: ValTensor::from(inp), output: ValTensor::from(out), _marker: PhantomData, @@ -1658,9 +1654,9 @@ mod rangecheckpercent { prover.assert_satisfied(); } { - let inp = Tensor::new(Some(&[Value::::known(Fp::from(200_u64))]), &[1]).unwrap(); - let out = Tensor::new(Some(&[Value::::known(Fp::from(199_u64))]), &[1]).unwrap(); - let circuit = MyCircuit:: { + let inp = Tensor::new(Some(&[Value::::known(F::from(200_u64))]), &[1]).unwrap(); + let out = Tensor::new(Some(&[Value::::known(F::from(199_u64))]), &[1]).unwrap(); + let circuit = MyCircuit:: { input: ValTensor::from(inp), output: ValTensor::from(out), _marker: PhantomData, @@ -1671,9 +1667,9 @@ mod rangecheckpercent { // Unsuccessful case { - let inp = Tensor::new(Some(&[Value::::known(Fp::from(100_u64))]), &[1]).unwrap(); - let out = Tensor::new(Some(&[Value::::known(Fp::from(102_u64))]), &[1]).unwrap(); - let circuit = MyCircuit:: { + let inp = Tensor::new(Some(&[Value::::known(F::from(100_u64))]), &[1]).unwrap(); + let out = Tensor::new(Some(&[Value::::known(F::from(102_u64))]), &[1]).unwrap(); + let circuit = MyCircuit:: { input: ValTensor::from(inp), output: ValTensor::from(out), _marker: PhantomData, @@ -1699,14 +1695,13 @@ mod relu { dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; - use halo2curves::pasta::Fp as F; #[derive(Clone)] struct ReLUCircuit { pub input: ValTensor, } - impl Circuit for ReLUCircuit { + impl Circuit for ReLUCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; @@ -1779,7 +1774,6 @@ mod softmax { dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; - use halo2curves::pasta::Fp as F; const K: usize = 18; const LEN: usize = 3; @@ -1791,7 +1785,7 @@ mod softmax { _marker: PhantomData, } - impl Circuit for SoftmaxCircuit { + impl Circuit for SoftmaxCircuit { type Config = BaseConfig; type FloorPlanner = SimpleFloorPlanner; type Params = TestParams; diff --git a/src/eth.rs b/src/eth.rs index 52fc9ab5..25b7f78c 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -247,7 +247,7 @@ pub async fn verify_proof_via_solidity( let encoded = func.encode_input(&[ Token::FixedArray(public_inputs.into_iter().map(Token::Uint).collect()), - Token::Bytes(proof.proof.into()), + Token::Bytes(proof.proof), ])?; info!("encoded: {:#?}", hex::encode(&encoded)); @@ -371,7 +371,7 @@ pub async fn verify_proof_with_data_attestation( let encoded = func.encode_input(&[ Token::FixedArray(public_inputs.into_iter().map(Token::Uint).collect()), - Token::Bytes(proof.proof.into()), + Token::Bytes(proof.proof), ])?; info!("encoded: {:#?}", hex::encode(&encoded)); diff --git a/src/execute.rs b/src/execute.rs index 5d4bf11e..effae71b 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -860,7 +860,7 @@ pub(crate) fn create_evm_verifier( let _ = f.write(output.as_bytes()); // fetch abi of the contract - let (abi, _, _) = get_contract_artifacts(sol_code_path.clone(), "Verifier", None)?; + let (abi, _, _) = get_contract_artifacts(sol_code_path, "Verifier", None)?; // save abi to file serde_json::to_writer(std::fs::File::create(abi_path)?, &abi)?; @@ -933,7 +933,7 @@ pub(crate) fn create_evm_data_attestation_verifier( let _ = f.write(output.as_bytes()); // fetch abi of the contract let (abi, _, _) = - get_contract_artifacts(sol_code_path.clone(), "DataAttestationVerifier", None)?; + get_contract_artifacts(sol_code_path, "DataAttestationVerifier", None)?; // save abi to file serde_json::to_writer(std::fs::File::create(abi_path)?, &abi)?; } else { @@ -1018,7 +1018,7 @@ pub(crate) fn create_evm_aggregate_verifier( let settings: Vec = circuit_settings .iter() - .map(|path| GraphSettings::load(&path).unwrap()) + .map(|path| GraphSettings::load(path).unwrap()) .collect::>(); let num_public_inputs: usize = settings @@ -1053,7 +1053,7 @@ pub(crate) fn create_evm_aggregate_verifier( let _ = f.write(output.as_bytes()); // fetch abi of the contract - let (abi, _, _) = get_contract_artifacts(sol_code_path.clone(), "Verifier", None)?; + let (abi, _, _) = get_contract_artifacts(sol_code_path, "Verifier", None)?; // save abi to file serde_json::to_writer(std::fs::File::create(abi_path)?, &abi)?; diff --git a/src/graph/mod.rs b/src/graph/mod.rs index e7001355..69b9f8d1 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -405,7 +405,7 @@ pub struct GraphConfig { } /// Defines the circuit for a computational graph / model loaded from a `.onnx` file. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize)] pub struct GraphCircuit { /// The model / graph of computations. pub model: Model, @@ -796,13 +796,13 @@ impl GraphCircuit { if visibility.params.requires_processing() { let params = self.model.get_all_consts(); - let flattened_params = flatten_valtensors(params)?; - let flattened_params = if !flattened_params.is_empty() { - vec![flattened_params[0].get_felt_evals()?.into_iter().into()] - } else { - vec![] - }; - processed_params = Some(GraphModules::forward(&flattened_params, visibility.params)?); + if !params.is_empty() { + let flattened_params = Tensor::new(Some(¶ms), &[params.len()])?.combine()?; + processed_params = Some(GraphModules::forward( + &[flattened_params], + visibility.params, + )?); + } } let model_results = self.model.forward(inputs)?; @@ -1003,40 +1003,53 @@ impl Circuit for GraphCircuit { &self.module_settings.input, )?; - // now we need to flatten the params - let mut flattened_params = - flatten_valtensors(self.model.get_all_consts()).map_err(|_| { - log::error!("failed to flatten params"); + // now we need to assign the flattened params to the model + let mut model = self.model.clone(); + let param_visibility = self.settings.run_args.param_visibility; + trace!("running params module layout"); + if !self.model.get_all_consts().is_empty() && param_visibility.requires_processing() { + // now we need to flatten the params + let consts = self.model.get_all_consts(); + + let mut flattened_params = { + let mut t = Tensor::new(Some(&consts), &[consts.len()]) + .map_err(|_| { + log::error!("failed to flatten params"); + PlonkError::Synthesis + })? + .combine() + .map_err(|_| { + log::error!("failed to combine params"); + PlonkError::Synthesis + })?; + t.set_visibility(param_visibility); + vec![t.into()] + }; + + // now do stuff to the model params + GraphModules::layout( + &mut layouter, + &config.module_configs, + &mut flattened_params, + param_visibility, + &mut instance_offset, + &self.module_settings.params, + )?; + + let shapes = self.model.const_shapes(); + trace!("replacing processed consts"); + let split_params = split_valtensor(&flattened_params[0], shapes).map_err(|_| { + log::error!("failed to split params"); PlonkError::Synthesis })?; - // now do stuff to the model params - GraphModules::layout( - &mut layouter, - &config.module_configs, - &mut flattened_params, - self.settings.run_args.param_visibility, - &mut instance_offset, - &self.module_settings.params, - )?; - - // now we need to assign the flattened params to the model - let mut model = self.model.clone(); - if !self.model.get_all_consts().is_empty() { // now the flattened_params have been assigned to and we-assign them to the model consts such that they are constrained to be equal - model.replace_consts( - split_valtensor(flattened_params[0].clone(), self.model.const_shapes()).map_err( - |_| { - log::error!("failed to replace params"); - PlonkError::Synthesis - }, - )?, - ); + model.replace_consts(split_params); } // create a new module for the model (space 2) layouter.assign_region(|| "_new_module", |_| Ok(()))?; - trace!("Laying out model"); + trace!("laying out model"); let mut outputs = model .layout( config.model_config.clone(), diff --git a/src/graph/model.rs b/src/graph/model.rs index 2c4f73a9..13569473 100644 --- a/src/graph/model.rs +++ b/src/graph/model.rs @@ -17,6 +17,8 @@ use crate::{ use halo2curves::bn256::Fr as Fp; use colored::Colorize; +use serde::Deserialize; +use serde::Serialize; use tract_onnx::prelude::{ DatumExt, Graph, InferenceFact, InferenceModelExt, SymbolValues, TypedFact, TypedOp, }; @@ -33,6 +35,9 @@ use log::{debug, info, trace}; use std::collections::BTreeMap; use std::collections::HashSet; use std::error::Error; +use std::fs; +use std::io::Read; +use std::path::PathBuf; use tabled::Table; use tract_onnx; use tract_onnx::prelude::Framework; @@ -59,7 +64,7 @@ pub struct ModelConfig { pub type NodeGraph = BTreeMap; /// A struct for loading from an Onnx file and converting a computational graph to a circuit. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] pub struct Model { /// input indices pub graph: ParsedNodes, @@ -68,7 +73,7 @@ pub struct Model { } /// Enables model as subnode of other models -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum NodeType { /// A node in the model Node(Node), @@ -146,7 +151,7 @@ impl NodeType { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] /// A set of EZKL nodes that represent a computational graph. pub struct ParsedNodes { nodes: BTreeMap, @@ -225,6 +230,25 @@ impl Model { Ok(om) } + /// + pub fn save(&self, path: PathBuf) -> Result<(), Box> { + let f = std::fs::File::create(path)?; + let writer = std::io::BufWriter::new(f); + bincode::serialize_into(writer, &self)?; + Ok(()) + } + + /// + pub fn load(path: PathBuf) -> Result> { + // read bytes from file + let mut f = std::fs::File::open(&path).expect("no file found"); + let metadata = fs::metadata(&path).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read_exact(&mut buffer).expect("buffer overflow"); + let result = bincode::deserialize(&buffer)?; + Ok(result) + } + /// Generate model parameters for the circuit pub fn gen_params( &self, @@ -793,7 +817,7 @@ impl Model { } /// Retrieves all constants from the model. - pub fn get_all_consts(&self) -> Vec> { + pub fn get_all_consts(&self) -> Vec> { let mut consts = vec![]; for node in self.graph.nodes.values() { match node { @@ -841,10 +865,13 @@ impl Model { .as_any() .downcast_ref::>() { - n.opkind = Box::new(crate::circuit::Constant::new( - consts[const_idx].clone(), + let mut op = crate::circuit::Constant::new( + constant.quantized_values.clone(), constant.raw_values.clone(), - )); + ); + op.pre_assign(consts[const_idx].clone()); + n.opkind = Box::new(op); + const_idx += 1; }; } diff --git a/src/graph/modules.rs b/src/graph/modules.rs index 6bd3b1b0..be428c9b 100644 --- a/src/graph/modules.rs +++ b/src/graph/modules.rs @@ -240,21 +240,21 @@ impl GraphModules { module_res: &Option, instances: &mut ModuleInstances, ) { - if visibility.is_hashed() { - instances - .poseidon - .extend(module_res.clone().unwrap().poseidon_hash.unwrap()); - } else if visibility.is_encrypted() { - instances.elgamal.extend( - module_res - .clone() - .unwrap() - .elgamal - .unwrap() - .ciphertexts - .into_iter() - .flatten(), - ); + if let Some(res) = module_res { + if visibility.is_hashed() { + instances + .poseidon + .extend(res.poseidon_hash.clone().unwrap()); + } else if visibility.is_encrypted() { + instances.elgamal.extend( + res.elgamal + .clone() + .unwrap() + .ciphertexts + .into_iter() + .flatten(), + ); + } } } diff --git a/src/graph/node.rs b/src/graph/node.rs index 47991d33..7f5c88fd 100644 --- a/src/graph/node.rs +++ b/src/graph/node.rs @@ -5,6 +5,8 @@ use crate::graph::new_op_from_onnx; use crate::graph::GraphError; use halo2curves::bn256::Fr as Fp; use log::trace; +use serde::Deserialize; +use serde::Serialize; use std::collections::BTreeMap; use std::error::Error; use std::fmt; @@ -28,10 +30,11 @@ fn display_opkind(v: &Box>) -> String { } /// A single operation in a [crate::graph::Model]. -#[derive(Clone, Debug, Tabled)] +#[derive(Clone, Debug, Tabled, Serialize, Deserialize)] pub struct Node { /// [Op] i.e what operation this node represents. #[tabled(display_with = "display_opkind")] + #[serde(with = "serde_traitobject")] pub opkind: Box>, /// The denominator in the fixed point representation for the node's output. Tensors of differing scales should not be combined. pub out_scale: u32, @@ -47,6 +50,16 @@ pub struct Node { pub idx: usize, } +impl PartialEq for Node { + fn eq(&self, other: &Node) -> bool { + (self.out_scale == other.out_scale) + && (self.inputs == other.inputs) + && (self.out_dims == other.out_dims) + && (self.idx == other.idx) + && (self.opkind.as_string() == other.opkind.as_string()) + } +} + impl Node { /// Converts a tract [OnnxNode] into an ezkl [Node]. /// # Arguments: diff --git a/src/graph/utilities.rs b/src/graph/utilities.rs index 2b262333..99c933d5 100644 --- a/src/graph/utilities.rs +++ b/src/graph/utilities.rs @@ -4,7 +4,7 @@ use super::{GraphError, Visibility}; use crate::circuit::hybrid::HybridOp; use crate::circuit::lookup::LookupOp; use crate::circuit::poly::PolyOp; -use crate::tensor::{Tensor, TensorError, TensorType, ValTensor, ValType}; +use crate::tensor::{Tensor, TensorError, TensorType}; use halo2curves::bn256::Fr as Fp; use halo2curves::ff::PrimeField; use log::{debug, warn}; @@ -309,7 +309,7 @@ pub fn new_op_from_onnx( let constant_scale = if dt == DatumType::Bool { 0 } else { scale }; // Quantize the raw value let quantized_value = - tensor_to_valtensor(raw_value.clone(), constant_scale, param_visibility)?; + quantize_tensor(raw_value.clone(), constant_scale, param_visibility)?; // Create a constant op Box::new(crate::circuit::ops::Constant::new( quantized_value, @@ -412,7 +412,7 @@ pub fn new_op_from_onnx( let boxed_op = inp.opkind(); if let Some(c) = extract_const_raw_values(boxed_op) { inputs.remove(idx); - params = Some(tensor_to_valtensor(c, max_scale, param_visibility)?); + params = Some(quantize_tensor(c, max_scale, param_visibility)?); } } @@ -557,13 +557,13 @@ pub fn new_op_from_onnx( (padding[0], padding[1], stride[0], stride[1]); let kernel = extract_tensor_value(conv_node.kernel.clone())?; - let kernel = tensor_to_valtensor(kernel, scale, param_visibility)?; + let kernel = quantize_tensor(kernel, scale, param_visibility)?; let bias = match conv_node.bias.clone() { Some(b) => { let const_value = extract_tensor_value(b)?; - let val = tensor_to_valtensor( + let val = quantize_tensor( const_value, scale + inputs[0].out_scales()[0], param_visibility, @@ -621,13 +621,13 @@ pub fn new_op_from_onnx( (padding[0], padding[1], stride[0], stride[1]); let kernel = extract_tensor_value(deconv_node.kernel.clone())?; - let kernel = tensor_to_valtensor(kernel, scale, param_visibility)?; + let kernel = quantize_tensor(kernel, scale, param_visibility)?; let bias = match deconv_node.bias.clone() { Some(b) => { let const_value = extract_tensor_value(b)?; - let val = tensor_to_valtensor( + let val = quantize_tensor( const_value, scale + inputs[0].out_scales()[0], param_visibility, @@ -805,7 +805,7 @@ pub fn extract_const_raw_values(boxed_op: Box>) -> Op /// Extracts the quantized values from a [crate::circuit::ops::Constant] op. pub fn extract_const_quantized_values( boxed_op: Box>, -) -> Option> { +) -> Option> { boxed_op .as_any() .downcast_ref::>() @@ -813,58 +813,23 @@ pub fn extract_const_quantized_values( } /// Converts a tensor to a [ValTensor] with a given scale. -pub fn tensor_to_valtensor( +pub fn quantize_tensor( const_value: Tensor, scale: u32, visibility: Visibility, -) -> Result, Box> { - let mut value: ValTensor = match visibility { - Visibility::Public => const_value - .map(|x| { - crate::tensor::ValType::Constant(crate::fieldutils::i128_to_felt::( - quantize_float(&x.into(), 0.0, scale).unwrap(), - )) - }) - .into(), - Visibility::Private | Visibility::Hashed | Visibility::Encrypted => const_value - .map(|x| { - crate::tensor::ValType::Value(halo2_proofs::circuit::Value::known( - crate::fieldutils::i128_to_felt::( - quantize_float(&x.into(), 0.0, scale).unwrap(), - ), - )) - }) - .into(), - }; +) -> Result, Box> { + let mut value: Tensor = const_value.map(|x| { + crate::fieldutils::i128_to_felt::(quantize_float(&x.into(), 0.0, scale).unwrap()) + }); value.set_scale(scale); + value.set_visibility(visibility); Ok(value) } -/// Flatten a vector of [ValTensor]s into a single [ValTensor]. -pub(crate) fn flatten_valtensors( - tensors: Vec>, -) -> Result>, Box> { - if tensors.is_empty() { - return Ok(vec![]); - } - - let mut merged: Vec> = tensors[0] - .get_inner_tensor()? - .into_iter() - .collect::>(); - - for tensor in tensors.iter().skip(1) { - let vals = tensor.get_inner_tensor()?.into_iter(); - merged.extend(vals); - } - - let tensor = Tensor::new(Some(&merged), &[merged.len()])?; - Ok(vec![tensor.into()]) -} - +use crate::tensor::ValTensor; /// Split a [ValTensor] into a vector of [ValTensor]s. pub(crate) fn split_valtensor( - values: ValTensor, + values: &ValTensor, shapes: Vec>, ) -> Result>, Box> { let mut tensors: Vec> = Vec::new(); @@ -890,13 +855,18 @@ pub mod tests { let tensor2: Tensor = (10..20).map(|x| x.into()).into(); let tensor3: Tensor = (20..30).map(|x| x.into()).into(); - let flattened = - flatten_valtensors(vec![tensor1.into(), tensor2.into(), tensor3.into()]).unwrap(); + let mut tensor = Tensor::new(Some(&[tensor1, tensor2, tensor3]), &[3]) + .unwrap() + .combine() + .unwrap(); - assert_eq!(flattened[0].len(), 30); + tensor.set_visibility(Visibility::Public); - let split = - split_valtensor(flattened[0].clone(), vec![vec![2, 5], vec![10], vec![5, 2]]).unwrap(); + let flattened: ValTensor = tensor.into(); + + assert_eq!(flattened.len(), 30); + + let split = split_valtensor(&flattened, vec![vec![2, 5], vec![10], vec![5, 2]]).unwrap(); assert_eq!(split.len(), 3); assert_eq!(split[0].len(), 10); diff --git a/src/graph/vars.rs b/src/graph/vars.rs index 0309d1da..d141fc16 100644 --- a/src/graph/vars.rs +++ b/src/graph/vars.rs @@ -101,7 +101,7 @@ impl std::fmt::Display for Visibility { } /// Represents whether the model input, model parameters, and model output are Public or Private to the prover. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, PartialOrd)] pub struct VarVisibility { /// Input to the model or computational graph pub input: Visibility, diff --git a/src/pfsys/evm/aggregation.rs b/src/pfsys/evm/aggregation.rs index 929cc878..bdf254a9 100644 --- a/src/pfsys/evm/aggregation.rs +++ b/src/pfsys/evm/aggregation.rs @@ -199,14 +199,14 @@ impl AggregationCircuit { trace!("Aggregating with snark instances {:?}", snark.instances); let mut transcript = PoseidonTranscript::::new(snark.proof.as_slice()); let proof = PlonkSuccinctVerifier::read_proof( - &svk, + svk, snark.protocol.as_ref().unwrap(), &snark.instances, &mut transcript, ) .map_err(|_| AggregationError::ProofRead)?; let mut accum = PlonkSuccinctVerifier::verify( - &svk, + svk, snark.protocol.as_ref().unwrap(), &snark.instances, &proof, @@ -231,7 +231,7 @@ impl AggregationCircuit { .concat(); Ok(Self { - svk: svk.clone(), + svk: *svk, snarks: snarks.into_iter().map_into().collect(), instances, as_proof: Value::known(as_proof), @@ -264,7 +264,7 @@ impl AggregationCircuit { for instance in snark_instance.iter_mut() { let mut felt_evals = vec![]; for value in instance.iter_mut() { - value.map(|v| felt_evals.push(v.clone())); + value.map(|v| felt_evals.push(v)); } instances[0].extend(felt_evals); } diff --git a/src/tensor/mod.rs b/src/tensor/mod.rs index 533650de..06aa4f22 100644 --- a/src/tensor/mod.rs +++ b/src/tensor/mod.rs @@ -14,6 +14,7 @@ pub use var::*; use crate::{ circuit::utils, fieldutils::{felt_to_i32, i128_to_felt, i32_to_felt}, + graph::Visibility, }; use halo2_proofs::{ @@ -265,6 +266,8 @@ impl TensorType for halo2curves::bn256::Fr { pub struct Tensor { inner: Vec, dims: Vec, + scale: Option, + visibility: Option, } impl IntoIterator for Tensor { @@ -446,15 +449,39 @@ impl Tensor { Ok(Tensor { inner: Vec::from(v), dims: Vec::from(dims), + scale: None, + visibility: None, }) } None => Ok(Tensor { inner: vec![T::zero().unwrap(); total_dims], dims: Vec::from(dims), + scale: None, + visibility: None, }), } } + /// set the tensor's (optional) scale parameter + pub fn set_scale(&mut self, scale: u32) { + self.scale = Some(scale) + } + + /// set the tensor's (optional) visibility parameter + pub fn set_visibility(&mut self, visibility: Visibility) { + self.visibility = Some(visibility) + } + + /// getter for scale + pub fn scale(&self) -> Option { + self.scale + } + + /// getter for visibility + pub fn visibility(&self) -> Option { + self.visibility + } + /// Returns the number of elements in the tensor. pub fn len(&self) -> usize { self.dims().iter().product::() diff --git a/src/tensor/val.rs b/src/tensor/val.rs index 60f1e58c..2dcd56af 100644 --- a/src/tensor/val.rs +++ b/src/tensor/val.rs @@ -130,7 +130,19 @@ impl From>> for ValTe impl From> for ValTensor { fn from(t: Tensor) -> ValTensor { ValTensor::Value { - inner: t.map(|x| x.into()), + inner: t.map(|x| + if let Some(vis) = t.visibility { + match vis { + Visibility::Public => x.into(), + Visibility::Private | Visibility::Hashed | Visibility::Encrypted => { + Value::known(x).into() + } + } + } + else { + panic!("visibility should be set to convert a tensor of field elements to a ValTensor.") + } + ), dims: t.dims().to_vec(), scale: 1, } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8af0fab9..80a51d11 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4,7 +4,7 @@ mod native_tests { use core::panic; use ezkl::graph::input::{FileSource, GraphData}; - use ezkl::graph::{DataSource, GraphSettings}; + use ezkl::graph::{DataSource, GraphSettings, Visibility}; use lazy_static::lazy_static; use std::env::var; use std::process::Command; @@ -302,6 +302,7 @@ mod native_tests { use crate::native_tests::kzg_prove_and_verify; use crate::native_tests::kzg_fuzz; use crate::native_tests::render_circuit; + use crate::native_tests::model_serialization; use crate::native_tests::tutorial as run_tutorial; #[test] @@ -312,8 +313,16 @@ mod native_tests { } + seq!(N in 0..=36 { + #(#[test_case(TESTS[N])])* + fn model_serialization_(test: &str) { + crate::native_tests::mv_test_(test); + // percent tolerance test + model_serialization(test.to_string()); + } + #(#[test_case(TESTS[N])])* fn render_circuit_(test: &str) { crate::native_tests::init_binary(); @@ -696,6 +705,26 @@ mod native_tests { test_func_examples!(); test_neg_examples!(); + fn model_serialization(example_name: String) { + let test_dir = TEST_DIR.path().to_str().unwrap(); + let model_path = format!("{}/{}/network.onnx", test_dir, example_name); + let serialization_path = format!("{}/{}/network.ezkl", test_dir, example_name); + let run_args = ezkl::commands::RunArgs { + param_visibility: Visibility::Public, + batch_size: 1, + ..Default::default() + }; + + let model = + ezkl::graph::Model::new(&mut std::fs::File::open(model_path).unwrap(), run_args) + .unwrap(); + + model.save(serialization_path.clone().into()).unwrap(); + + let loaded_model = ezkl::graph::Model::load(serialization_path.into()).unwrap(); + assert_eq!(model, loaded_model) + } + // Mock prove (fast, but does not cover some potential issues) fn neg_mock(example_name: String, counter_example: String) { let test_dir = TEST_DIR.path().to_str().unwrap(); diff --git a/tests/py_integration_tests.rs b/tests/py_integration_tests.rs index 333c1b7c..76e3bc22 100644 --- a/tests/py_integration_tests.rs +++ b/tests/py_integration_tests.rs @@ -75,7 +75,7 @@ mod py_tests { ]) .status() .expect("failed to execute process"); - + assert!(status.success()); let status = Command::new("pip") .args(["install", "numpy==1.23"]) .status() @@ -168,7 +168,7 @@ mod py_tests { "--to", "notebook", "--execute", - &path.to_str().unwrap(), + (path.to_str().unwrap()), ]) .status() .expect("failed to execute process"); diff --git a/tests/wasi_integration_tests.rs b/tests/wasi_integration_tests.rs deleted file mode 100644 index 6418976d..00000000 --- a/tests/wasi_integration_tests.rs +++ /dev/null @@ -1,186 +0,0 @@ -#[cfg(test)] -mod wasi_tests { - use lazy_static::lazy_static; - use std::env::var; - use std::process::Command; - use std::sync::Once; - use tempdir::TempDir; - static COMPILE: Once = Once::new(); - - lazy_static! { - static ref CARGO_TARGET_DIR: String = - var("CARGO_TARGET_DIR").unwrap_or_else(|_| "./target".to_string()); - static ref TEST_DIR: TempDir = TempDir::new("example").unwrap(); - } - - fn mv_test_(test: &str) { - let test_dir = TEST_DIR.path().to_str().unwrap(); - let path: std::path::PathBuf = format!("{}/{}", test_dir, test).into(); - if !path.exists() { - let status = Command::new("cp") - .args([ - "-R", - &format!("./examples/onnx/{}", test), - &format!("{}/{}", test_dir, test), - ]) - .status() - .expect("failed to execute process"); - assert!(status.success()); - } - } - - fn init() { - COMPILE.call_once(|| { - println!("using cargo target dir: {}", *CARGO_TARGET_DIR); - build_ezkl_wasm(); - }); - } - - const TESTS: [&str; 19] = [ - "1l_mlp", - "1l_flatten", - "1l_average", - "1l_div", - "1l_pad", - "1l_reshape", - "1l_sigmoid", - "1l_sqrt", - "1l_leakyrelu", - "1l_relu", - "2l_relu_sigmoid_small", - "2l_relu_fc", - "2l_relu_small", - "2l_relu_sigmoid", - "1l_conv", - "2l_sigmoid_small", - "2l_relu_sigmoid_conv", - "3l_relu_conv_fc", - "4l_relu_conv_fc", - ]; - - macro_rules! wasi_test_func { - () => { - #[cfg(test)] - mod tests_wasi { - use seq_macro::seq; - use crate::wasi_tests::TESTS; - use test_case::test_case; - use crate::wasi_tests::mock; - - seq!(N in 0..=18 { - - #(#[test_case(TESTS[N])])* - fn mock_public_outputs_(test: &str) { - crate::wasi_tests::init(); - crate::wasi_tests::mv_test_(test); - mock(test.to_string(), 7, 16, 17, "private", "private", "public", 1); - } - - #(#[test_case(TESTS[N])])* - fn mock_public_inputs_(test: &str) { - crate::wasi_tests::init(); - crate::wasi_tests::mv_test_(test); - mock(test.to_string(), 7, 16, 17, "public", "private", "private", 1); - } - - #(#[test_case(TESTS[N])])* - fn mock_public_params_(test: &str) { - crate::wasi_tests::init(); - crate::wasi_tests::mv_test_(test); - mock(test.to_string(), 7, 16, 17, "private", "public", "private", 1); - } - - - }); - - } - }; -} - - wasi_test_func!(); - - #[allow(clippy::too_many_arguments)] - // Mock prove (fast, but does not cover some potential issues) - fn mock( - example_name: String, - scale: usize, - bits: usize, - logrows: usize, - input_visibility: &str, - param_visibility: &str, - output_visibility: &str, - batch_size: usize, - ) { - let test_dir = TEST_DIR.path().to_str().unwrap(); - - let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR)) - .args([ - "gen-settings", - "-M", - format!("{}/{}/network.onnx", test_dir, example_name).as_str(), - &format!( - "--settings-path={}/{}/settings.json", - test_dir, example_name - ), - &format!("--bits={}", bits), - &format!("--logrows={}", logrows), - &format!("--scale={}", scale), - &format!("--batch-size={}", batch_size), - &format!("--input-visibility={}", input_visibility), - &format!("--param-visibility={}", param_visibility), - &format!("--output-visibility={}", output_visibility), - ]) - .status() - .expect("failed to execute process"); - assert!(status.success()); - - let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR)) - .args([ - "gen-witness", - "-D", - &format!("{}/{}/input.json", test_dir, example_name), - "-M", - &format!("{}/{}/network.onnx", test_dir, example_name), - "-O", - &format!("{}/{}/witness.json", test_dir, example_name), - &format!( - "--settings-path={}/{}/settings.json", - test_dir, example_name - ), - ]) - .status() - .expect("failed to execute process"); - assert!(status.success()); - - let status = Command::new(format!("{}/release/ezkl", *CARGO_TARGET_DIR)) - .args([ - "mock", - "-W", - format!("{}/{}/witness.json", test_dir, example_name).as_str(), - "-M", - format!("{}/{}/network.onnx", test_dir, example_name).as_str(), - &format!( - "--settings-path={}/{}/settings.json", - test_dir, example_name - ), - ]) - .status() - .expect("failed to execute process"); - assert!(status.success()); - } - - fn build_ezkl_wasm() { - let status = Command::new("cargo") - .args([ - "build", - "--release", - "--bin", - "ezkl", - "--target", - "wasm32-wasi", - ]) - .status() - .expect("failed to execute process"); - assert!(status.success()); - } -} diff --git a/tests/wasm/test.key b/tests/wasm/test.key index d8cc0b9091c1a438b55819f956cb829b34a7f980..9a843aa091b97b5c8bbf921857b43a1cbe9ad985 100644 GIT binary patch delta 578 zcmV-I0=@m92cQR#$bZo}hk24{;`-CR3P)O8sRdpU%8B2!N9tl)N05nB!~+1QK9Hwk zvu-isXXgUPU|MAbmCvdAU;tX&m}KXFZ5=o2#AR3O z4q;$t)(`;3cJ)eT$hN5$P`4HF<4dH_;rmTj^u0z>nkLAZkj;6Os-K$O4*^QHjGqHB zG`lanQEOapQE%s}Vkr6kMAPIlX2Y&Xb!^It3B2x^l_9QJ3PjoRVag;#6DM?JK#&rW6 zqf$sjrtjZsyN(JbxwJ}Ek0Bh?`vXzNl?K-=fQu0~WkE$ybY2)tiO=q_9EE)_!+Qr!OWS7czegDe*1?9}0cT zS9@*V%;F`J-?cp$wAwl8(AAKq^K^0^j!(W!A(+7VSwjyxQ6|^QQEtn(6K}}y3FQm? zvBCol&QUT<^~m?eI$pA&Emk=3|EDjjf{gQYg6|F-UY8*Znv^P-+9F23W7uT2q{AWaVU1;Rl@=jERzTWK@tD}@Bjb+0000000000003aykr5URkR$*A00000 Q000009+43q1RekY0JhBo-v9sr delta 578 zcmV-I0=@m92cQR#$bXA1-QW6zz5_X2PhMzzV0Bw-hN8*tb#C#*N|=C9uL$R3wYV9l z=7AIq*dMQYkYQVWyTM5DiQGb)J0~!_ZxpzUCNDnF&ya(83PSb<#z+`14oFoL$p_2I z;m>qzPz!-KRw_VxK|d?)4EuBtd-oWUy8n(M**!zTzIL9@dVdsk^$tNo!A>iW`GvBD z0GQ*$tg2ltyFc%=+j)#?dgLcJe8DaWIve%)%%obs(_}OiI)pSE9R?8PDZhd{;727l zzqaY_9)AoD`6xQdI|QS?N+JX?ybYJ+0R6-zlHUdp66e~|zYYywf3D8Q;1G%nYwpVI znAX6jZ;e9Q=K`81kr6kMAPIj_rSg)gjG2T+FAIe~;+vcvgL<9(xWH1{a2#R`op=&0 z*|CxZ(Wb&uf@He*wF_63g#j?O?dq>zROg}(?bDqyTG)u5RQdAva3ih<{pgGX0$j5&QgxTb+t28y< z25beV_CmRj+0!-J-HWr}A$&sQ3jO{DMrNSIAp&JSWi$xwgLqMDK_9T`wl5c!F_HOdG?U6Zma z*tOJH@5grB)000000F&MWLI3~& diff --git a/tests/wasm/test.proof b/tests/wasm/test.proof index 665019d5..e34893e0 100644 --- a/tests/wasm/test.proof +++ b/tests/wasm/test.proof @@ -1 +1 @@ -{"instances":[[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]],"proof":"0714e01c6e55f4c3dd84ce956aee793e6ea926e4c2d630d787fbf33afd601f540e7f713eb77806f5f1f525b850ebf15d6e42db791190397708ad32379aacb15b2789a167f27e11545183aba92dadf001508b9645c0711699eff327a336473a490826a506104ebf5dc15a75807d8c29d52c9ab8862cf45da8378cfcb17252e7ef1b95c433c896d3a00093342512831a647e6149c9e935f407b6ce5ec988971d2310aebb486ca8df29f1ded7b6ecc1b8daed0325d8e5f65d1d32c766f98847f6e413962767808744c7c2e60e18095fd9e3776b9596bef5fca571fb2c77c07242e600711fffc1992570d88eda23a89d71d305d8af5a71e07e88816e371b5d630e71183f4da4b47b078eb657c97dca1ea9d0db746372babc59e82c74677ade733e1f024f63b83cb0ca225866fb8a24efa8bf8c2451f9809307ad678f5aeef17474e900f5b168a7631554c5dfa2180a0aa52021eb7079d96d847e2af192f7e4dd494a127e07148154f1a9b94da423fdd254bbd324a2cabebe2832d77604f5b6288eee1bc202658008ac680cd0a87d0bce5bdb2dafec07996b9efb2e10f07f49a66f040cf0ee8ed787329c50e09f86f1f5384092f7e313c1bab7b79af336ca2d1e8447063d2359461f6669dfa50da7b5ee3bae5ad805781764ea52555e87d2658de2ff2746ce7bf061e5ca3b573b8609954e99c63024b29a30b79f087b6241f82ede68069394379a09728a1e084416f686a55b4f941aba20c417e120bd50d2e63ae0b91f887dcc172bb4727f87add9756bec4fa14b29f9f034eb2c342c6529b36b9ddd1499c72b4a14c9ac5249399f931a5b6b752b2fbb8764821205cab25d50e7886f0c5f25754a7846bab301edd597c4d6ce57944d3353422d1180cd3f4852d51a2001cd4728f7f5f6e7f606abb99bda68264a9142193ea1c50a2aec4e1da29457e4305d9ad8ba646473115e917f26c6d52b344de9424c80042f293236bc0335b42908be1f2b9cf6a4fba743026ef124b8a14a30a6cdd89df0959c02a9b3350c77f501a28a9254a055e3fbfec93438bbdac4f3f35769e044bf66d17a37e91cfad16b0915da83ae95938405872e853f6b051234b8f05f6686808a984ee0b467ff97c021cefeb0aa730dcf987baa949dca9fdecd47fc5fe057a2dfa8dc383b208095e52b71ea1d947f730b292e99803138bb1cfe09336add3404b44947807998dcd7fe00309418b3e46092722178b51feead5b738463e83eb0d44d40c0a2a137a49c250f5ce0eee372edf9b50fbe0e39d4f9a2baa48167475396c4b6181e75f3e92e1a03da7ec2991bb1c773d657a2f7d853a7877b563af7c0526ff31478d1756792a600000000000000000000000000000000000000000000000000000000000000002ba6b8c23fc3b2c34f7eca781e627d952a652d004c14dd0d6ad6419dbac773811225c1d77912c52462befe12a4edca37c731a198f3118d9499b36fe99f75e48d1b05c45af53936542ccf2e25daee7d33ecc81e8ceff07f1746074712cde9a29b1bd57ad359ca8e5b43d8f5fe1ed413a5ed9ce5e712b0b6d2eef54ded68964842090980e838149ab9b10cb515f078fc800e3f72cf1ee96e8e7a9366b152e1dd7a1864b91953f6e31950d51641abe91c721c3f2ad87b18ec1a1868b7503016f5fa000000000000000000000000000000000000000000000000000000000000000012ec7e19f1d8d6fb890df6401c2b27404c78554c959872a040e44f275a1441252c6d9a8feede7e0c86d5515c6944576e523fedf4506c480acd7db5cd00023d85209a7ec246a3255ab47613d19a1b4e049877e36e7304fa2e3121ece09ebc4c4b0eeb00a92c45d1167f03772108b733114b1f37d7e59961eb8766b44e44c5600004a28a818d328724c6e58202acc506f1987f40a10dc6bfc6ae49230f67c203681bbb022b4a68572ebff832222f9100732f8a133406cb76a893a32ef170d663a220d71188d85d2e8f4f968bfbeebcb3e3beaae1aa3225f87a4bd88c10ecffcfd4046f277a788b06ec0f84596b23059238bec964843f75300cac8c68c6557a12a02d525ba2a914ac4962cd4b117992093420058b3af6ae829c13548052d670116326d618f88ecdb9f2804d9de4bcba5d32cb1b0f3bd85ae9dfef342dcb9420853a24491c8150b7185621300e0dd1d03e5025f77ac3f14e4a373df985db4618ce1617801c619a15e747b14928cfca05096ad0e6d0d0a6cb7a0eaaa490b528d3b6772e3e4392268ef3a09ede2610e9e4fd2d8f6d84686aa813f17033a61bda9f794b18b46d6ea0b0def6567dc16a01e9fbbbc35a3776f329f96506424ab568c552a20cb8b14ec25645efd3e250434ac993e09e082fce3a93977735affab4b987d72f030b22ec536c0843db44777b3f0f8ecc308930f05898b82229e1c570e2b101bf0a617c3482bbf7d6f7c66f9c2fbd232c89ade9346161fe496b6363db6db97e480d064e43ac5e401d165606eb0993649f4a558d6477cc30baf02fe13d7e3fe73610be0fac7423e87002bb916f70c7995d74ca59af320b7d496bdadac09ee557ae07da1e5e84e465a0a6f897f572dc652b7eaa657733bc9f29472182e4a1e5a33615e9645e15f558af6c6302e91ac05686f2d19191dbce4b54804d8a2fcbc9f546189c22c576ea9898167b0eaec1e8989052d580b71fa4b87ea3cd826264198a121eb4178464351391770b840eea2452ff7f78671761aff3f46fb5897ac8f31e481173122357f7fa67aa3b8583552e941ea59e760d550c5cfe718bf93f471022bc","transcript_type":"EVM","protocol":{"domain":{"k":6,"n":64,"n_inv":[0,0,0,288230376151711744],"gen":[13065489725457997378,11211756437640344014,7238224585957573902,708111906564994541],"gen_inv":[12835574056175175549,9273301601072327479,17618698331133403535,2628560312543510721]},"preprocessed":[{"x":[0,0,0,0],"y":[0,0,0,0]},{"x":[15892520050510230344,17374574942102605775,5318356770787522907,1787743409480761049],"y":[6739641715635189184,3855943783057599807,7713725374243272979,802902418015120106]},{"x":[16363051564853346422,7795052860770989663,8556478298616703674,2748138783290834923],"y":[13087487910584912942,9135413512532872644,7187901193922462619,2445734187906676520]},{"x":[269799240721313163,8457896887902493753,7959530466229775195,625807386468861169],"y":[9360352604451070951,7030253910897462548,15891498054943735131,1472602518176111426]},{"x":[10434787827138333880,5243886470859225475,561001985098002200,815270781513747147],"y":[4558058713882441601,17832865244117789995,4456631303830934040,1475719007909626435]},{"x":[3120644082117834101,16399858617295436175,17239703702116936899,2874557060211137460],"y":[17661774174777080887,3775375206456282360,1154642357149514261,2686362248951048677]},{"x":[900472461969506103,11746579467834292494,10884562793133132478,495275174950404324],"y":[940900284503626256,9948698725632081759,13895461843082570507,2853848238498082727]},{"x":[0,0,0,0],"y":[0,0,0,0]},{"x":[9554822744385955153,11284862053749829446,5962969301751857950,1330986366399181018],"y":[14026127911358159149,6272306985927213650,12604147873894991253,3125543562542930070]},{"x":[14203375460262205293,8951859340791568608,16836662760394012077,91149279142067937],"y":[9623647809165114225,10096760505368293947,9689903759055906259,776809471815180597]},{"x":[16011761962147174788,10356382445246704646,16191438870292059097,2604491313407753249],"y":[2235925118587189532,2549398281859761352,965448928044931369,1885530188693116939]},{"x":[16466837142124256480,18298024464694808413,4711345539135674440,3067684320266113225],"y":[13513606005414435101,3148741118268066357,5101808661032121816,2616583518015071714]},{"x":[2053425909763113988,8635405453899921881,6754106819062520147,3161518507209143189],"y":[3921084984809054090,1873649054155158520,17501099108056964912,1265681626307071133]}],"num_instance":[4],"num_witness":[3,2,4],"num_challenge":[1,2,1],"evaluations":[{"poly":14,"rotation":0},{"poly":15,"rotation":0},{"poly":16,"rotation":0},{"poly":16,"rotation":-1},{"poly":0,"rotation":0},{"poly":1,"rotation":0},{"poly":2,"rotation":0},{"poly":3,"rotation":0},{"poly":4,"rotation":0},{"poly":5,"rotation":0},{"poly":6,"rotation":0},{"poly":7,"rotation":0},{"poly":22,"rotation":0},{"poly":8,"rotation":0},{"poly":9,"rotation":0},{"poly":10,"rotation":0},{"poly":11,"rotation":0},{"poly":12,"rotation":0},{"poly":19,"rotation":0},{"poly":19,"rotation":1},{"poly":19,"rotation":-6},{"poly":20,"rotation":0},{"poly":20,"rotation":1},{"poly":21,"rotation":0},{"poly":21,"rotation":1},{"poly":17,"rotation":0},{"poly":17,"rotation":-1},{"poly":18,"rotation":0}],"queries":[{"poly":14,"rotation":0},{"poly":15,"rotation":0},{"poly":16,"rotation":0},{"poly":16,"rotation":-1},{"poly":19,"rotation":0},{"poly":19,"rotation":1},{"poly":20,"rotation":0},{"poly":20,"rotation":1},{"poly":19,"rotation":-6},{"poly":21,"rotation":0},{"poly":17,"rotation":0},{"poly":18,"rotation":0},{"poly":17,"rotation":-1},{"poly":21,"rotation":1},{"poly":0,"rotation":0},{"poly":1,"rotation":0},{"poly":2,"rotation":0},{"poly":3,"rotation":0},{"poly":4,"rotation":0},{"poly":5,"rotation":0},{"poly":6,"rotation":0},{"poly":7,"rotation":0},{"poly":8,"rotation":0},{"poly":9,"rotation":0},{"poly":10,"rotation":0},{"poly":11,"rotation":0},{"poly":12,"rotation":0},{"poly":23,"rotation":0},{"poly":22,"rotation":0}],"quotient":{"chunk_degree":1,"num_chunk":4,"numerator":{"DistributePowers":[[{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Product":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]},{"Polynomial":{"poly":16,"rotation":-1}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Product":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Polynomial":{"poly":16,"rotation":-1}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Negated":{"Polynomial":{"poly":16,"rotation":0}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Polynomial":{"poly":15,"rotation":0}}]},{"Product":[{"Polynomial":{"poly":7,"rotation":0}},{"Product":[{"Polynomial":{"poly":15,"rotation":0}},{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Negated":{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]}}]}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":19,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":-6}},{"Sum":[{"Product":[{"Polynomial":{"poly":20,"rotation":0}},{"Polynomial":{"poly":20,"rotation":0}}]},{"Negated":{"Polynomial":{"poly":20,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Polynomial":{"poly":20,"rotation":0}},{"Negated":{"Polynomial":{"poly":19,"rotation":-6}}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Polynomial":{"poly":19,"rotation":1}},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":8,"rotation":0}}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":9,"rotation":0}}]}]},{"Challenge":2}]}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":10,"rotation":0}}]}]},{"Challenge":2}]}]}]},{"Negated":{"Product":[{"Polynomial":{"poly":19,"rotation":0}},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[11100302345850292309,5109383341788583484,6450182039226333095,2498166472155664813]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[11922143911221101039,4762855335879493275,9634852812984583437,2104342265551292894]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]}]}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Polynomial":{"poly":20,"rotation":1}},{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":13,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":11,"rotation":0}}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":0,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":12,"rotation":0}}]}]},{"Challenge":2}]}]}]},{"Negated":{"Product":[{"Polynomial":{"poly":20,"rotation":0}},{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":13,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[6393076176221150738,7283021187728417300,15472188617747294665,3366061389777165561]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":0,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[5185504448716010194,13473122879869045206,7110214824824105482,296185565312886903]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]}]}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":21,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":-6}},{"Sum":[{"Product":[{"Polynomial":{"poly":21,"rotation":0}},{"Polynomial":{"poly":21,"rotation":0}}]},{"Negated":{"Polynomial":{"poly":21,"rotation":0}}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Product":[{"Polynomial":{"poly":21,"rotation":1}},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Challenge":1}]}]},{"Sum":[{"Polynomial":{"poly":18,"rotation":0}},{"Challenge":2}]}]},{"Negated":{"Product":[{"Product":[{"Polynomial":{"poly":21,"rotation":0}},{"Sum":[{"DistributePowers":[[{"Sum":[{"Product":[{"Polynomial":{"poly":3,"rotation":0}},{"Polynomial":{"poly":14,"rotation":0}}]},{"Scaled":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":3,"rotation":0}}}]},[0,0,0,0]]}]},{"Sum":[{"Product":[{"Polynomial":{"poly":3,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]},{"Scaled":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":3,"rotation":0}}}]},[0,0,0,0]]}]}],{"Challenge":0}]},{"Challenge":1}]}]},{"Sum":[{"DistributePowers":[[{"Polynomial":{"poly":1,"rotation":0}},{"Polynomial":{"poly":2,"rotation":0}}],{"Challenge":0}]},{"Challenge":2}]}]}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":18,"rotation":0}}}]}]},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":18,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":17,"rotation":-1}}}]}]}],{"Challenge":3}]}},"transcript_initial_state":[2309380771169542096,18096926655601500614,71412695481535949,3148156731921140739],"instance_committing_key":null,"linearization":null,"accumulator_indices":[]}} \ No newline at end of file +{"instances":[[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]],"proof":"0d14727cd95987ba78785a7eb48e9c6d5b3953e0de87cee5656871f7fae2674f0e5e05b0a9cd50995d1d2c69ae612aab36d1b69c61c30826f70ef638deb20983156d71f29910082e38ee862b5b392c33033fb249486c38b6272bde27e6f67969147d6be2c5a1f35dd26cb13da126a51e3f88b691be4f83d8e589b0b4d016f56629c642b6ad98d9c7758c99f315d16422e01203fa1091cfeafd19228558b1e3010c13b6eb63cc8b5c3a6e2a07175baa2cf376efca85fd5867f621a51e43650e740296f0330bc158e6f10334eb6f37acc279f84c05a71817b725c800ea488bb2020a0f2d2eed490fbcc47891e524536fdd10f7f932996730ac90ed50b8d839bc4c26174569978c389c09f58e113686a6c704f2dc03f3938e20ed425278296d10941114df1e8b57c5b0d2c27f87ead2a594f9c34160281cc5b91aec6c94389b992e0ee90e609751045b37ad3ec4e58788dc7622a1b6a588ca33f97388c12fc615381065ed07b7b522392224c27dd50671f541818306618420d494886fa5c6d599601490384485155dd25f5d2c81d4bc7797836acb921ba9110e6c48385366fdcecd0636a2ffcc02c7072862b61373e21975fac694a556640ba30e64ccd78149e5042b57e8424ce9370c7a75597f824ca057e92b965ecac9cd26f0d5c0130df965be003c8b23fdff269814cc43c4e678e1c889652afb63912753b73193a1e754baa31e298a3d251eb9a6c70eebbb6175e666facd07e2b3b241fd37e5c676f32231c62a8ee5b010b079b1bc069e3ababe161b2b095ff58475b9aa199c5d4b32a5153f20a0d0d8763eaac3151cbf4db0405e4919abf1eafed993df5e9dd6494a2b807e075a90e35819c3a600ede73fd361745abb548cd339c465f3e5b04353b728fab12e7fef3822693b552e1dbffd0b4509b696e8dd29eeaa663c8f0e5e3522d4559d111c771c5a46f8817da57d13caaf9d5e834f9b163002dde45b3cf08ad890f49f0a2fab4fde4fc49e06d46ad564efd58f9fc93fc3f9f4ccbed7343208a1d946690f19b7779a4e0994134765ab34692b4f2c83a7d28ea720f7011563220f7b4cf214a0d0d0da3ba9f7c04b56d11f75d837b77e959454b171bb814005ee125248b109ba3fffd73df4765b8ee726af046bbadc090e044c75debc106b46f886838e3e028fcc450c8209edae7f023d252e7645b5448e1509827f751d9c47aef14e63be2a1479ffe7e5f8d2277d8d080a6f563e12443efa4f68e58766ecfc5d63251ab5217eedc3f656791f7ddf8646f21f7dff92011b38d625ecf55856a49b2c409f1f2343b47906ad5df1959fb237ea6d7970eb894fa24ecef5f6044b37d73ee6c97a00000000000000000000000000000000000000000000000000000000000000000d1f9fa8b2eaffb104124ca6a749e6fa7e953ce7db6bd0f76d98623ac969941e27231684059a51cd38231da05f167414de166d7159123648d431ec151b45c9aa1cf594b4081db9c9954871c26382eddb5bc448ea3fe5a8c2a9f4436530003a6419c60d34cc03891de3e5173057666077751d9dfb51a129c05e1098cd8f51555103eb352bf5b4677075c40092a5fe957326c6918a0a28255ca369f5793a3832b92686324a7bda56ec16ba858242a386949297c4d893c19a0ecd496e0552b8245700000000000000000000000000000000000000000000000000000000000000001cc8b5d56e277b2a5af1b46bd3e91bfe103a5719744d801ddbe46f0ee76fd9db11cdbecac32c320bc7c8fd85d443328b7ce0037ff7cc2af236b84855fccac15c05d030e14b04f55264ff2c09db98c19377720409a1e5742c8102afb6f742b36f1d5826412da6828c70f520651827ac47fdc0dd2ea6f2a1ad25ab57afd8b2f0f713cf4c57b992e029a465bd11661c53b88bc47edcbab15018d344c176d42c5207098059740720dc447864ad48511444b45ef510e395d870d5d1e36e0d05bfe1e408836b41fde46c669939220e688d8f2b03a13bad29b4a8a8e65f4783c317bd092e7c937ac130aae997f3abb3c51f8e49e5db8908bfcb7879d451a88ce12429b00b031f6aa4f2784baede58dc19a3ad06ffb784b81c22b6da91203f2fd1cdd03d15b6a481c1768e72f87fc3630534ef3f979fb63f21a83f3eea0e9ac10e0cda32195b48731a941f14064600e72a5664d12d9996cbe3b745427392165b2a1af840183174b7599e3331fe4e5c1958b29574a8d6748d69f93f05700fec1a531de98206f54d380ee51f5d25b479417df40b10500356be23d3c8d9807bebd035fb1fd706c7b4acc37fb7b02d69e8b32c0f7a69f4fdba05492ff2ac3c904b366688b2ba08cf528152e7ccf5a7c53399aa2ee58771a925ae22347b5a5f3096cdb5c0a80c1158867ce8b6c1126ba3cd3916220e97821ccfb3bb227f2829438f891e6ed4101e038a5904bfd30e03380df59c467e04cb1b6dd0c5ba2f6415b944b5d641f60e0b13628cfbd79dced1caee06bb3af616303779e66c3e1866ce8e918ca173ea00253b064ca3241d7792d95b6cc1f578b330169cfae8dd88ca6c9e854c0624c6fc28cec21aa0e5a17eda2ac613319e201eafd9b002fb66e4f112af8e9d3a3ae2950f9b1c63e990f8010f1a56302ebc2fc2425fa2c54c8a49094989cb175f112eab2ae43b1c6d5fd489279b66c4722bcd652e3ff9424c5b1dbeec33dac93144602e13f4986e22537227cb4e06aaec12ab8064d93af995ca7e974f6e1f17b51771e11e06827c2c0d1dc307e7eb443522111c85dedd39c8172fd3b71ec3e675bc1e58","transcript_type":"EVM","protocol":{"domain":{"k":6,"n":64,"n_inv":[0,0,0,288230376151711744],"gen":[13065489725457997378,11211756437640344014,7238224585957573902,708111906564994541],"gen_inv":[12835574056175175549,9273301601072327479,17618698331133403535,2628560312543510721]},"preprocessed":[{"x":[0,0,0,0],"y":[0,0,0,0]},{"x":[15892520050510230344,17374574942102605775,5318356770787522907,1787743409480761049],"y":[6739641715635189184,3855943783057599807,7713725374243272979,802902418015120106]},{"x":[16363051564853346422,7795052860770989663,8556478298616703674,2748138783290834923],"y":[13087487910584912942,9135413512532872644,7187901193922462619,2445734187906676520]},{"x":[18078126832532011473,407958784062439123,16881660600404021598,271434827356920418],"y":[7976827836822365952,6512423876463944241,27297205003289957,2120491631363284058]},{"x":[4042182506291063351,17762436592368288829,563765780385385476,189391611199961240],"y":[7426883431255098890,17552770502768534744,16053906895097945438,928636482639766197]},{"x":[15707345497003175353,6944847732196239726,5401353920877483623,1564807458096334950],"y":[5619333246300709873,14422384534865441879,11141811562108260505,261082017964298205]},{"x":[6085740493390165041,16280591500755705125,1231817382749998078,777845521977218103],"y":[14116331477026245918,7924134066871610406,272301758049638237,1481909996718938050]},{"x":[0,0,0,0],"y":[0,0,0,0]},{"x":[2145335927969286093,3252996053964890716,4653468741012836112,249323789115533759],"y":[16136298802093138716,5383131027651607402,5837786430425894741,1264245050482005446]},{"x":[1755968792829191479,9116724571222804812,4912151205534017703,1231646927750292988],"y":[8013431788850405672,9987230650743940402,8610555232284450477,878790299842750285]},{"x":[14662887090101874985,10603130267246623575,16805703951545382367,2193944558320014800],"y":[17996551379761385358,14616193245962519385,17350239694923460177,937807217116833033]},{"x":[14337148894628172238,17381737515822767674,8427233820585994239,874146233140047746],"y":[13782742031865123994,3615121272283125859,7411366692924579120,3175815460888651889]},{"x":[2053425909763113988,8635405453899921881,6754106819062520147,3161518507209143189],"y":[3921084984809054090,1873649054155158520,17501099108056964912,1265681626307071133]}],"num_instance":[4],"num_witness":[3,2,4],"num_challenge":[1,2,1],"evaluations":[{"poly":14,"rotation":0},{"poly":15,"rotation":0},{"poly":16,"rotation":0},{"poly":16,"rotation":-1},{"poly":0,"rotation":0},{"poly":1,"rotation":0},{"poly":2,"rotation":0},{"poly":3,"rotation":0},{"poly":4,"rotation":0},{"poly":5,"rotation":0},{"poly":6,"rotation":0},{"poly":7,"rotation":0},{"poly":22,"rotation":0},{"poly":8,"rotation":0},{"poly":9,"rotation":0},{"poly":10,"rotation":0},{"poly":11,"rotation":0},{"poly":12,"rotation":0},{"poly":19,"rotation":0},{"poly":19,"rotation":1},{"poly":19,"rotation":-6},{"poly":20,"rotation":0},{"poly":20,"rotation":1},{"poly":21,"rotation":0},{"poly":21,"rotation":1},{"poly":17,"rotation":0},{"poly":17,"rotation":-1},{"poly":18,"rotation":0}],"queries":[{"poly":14,"rotation":0},{"poly":15,"rotation":0},{"poly":16,"rotation":0},{"poly":16,"rotation":-1},{"poly":19,"rotation":0},{"poly":19,"rotation":1},{"poly":20,"rotation":0},{"poly":20,"rotation":1},{"poly":19,"rotation":-6},{"poly":21,"rotation":0},{"poly":17,"rotation":0},{"poly":18,"rotation":0},{"poly":17,"rotation":-1},{"poly":21,"rotation":1},{"poly":0,"rotation":0},{"poly":1,"rotation":0},{"poly":2,"rotation":0},{"poly":3,"rotation":0},{"poly":4,"rotation":0},{"poly":5,"rotation":0},{"poly":6,"rotation":0},{"poly":7,"rotation":0},{"poly":8,"rotation":0},{"poly":9,"rotation":0},{"poly":10,"rotation":0},{"poly":11,"rotation":0},{"poly":12,"rotation":0},{"poly":23,"rotation":0},{"poly":22,"rotation":0}],"quotient":{"chunk_degree":1,"num_chunk":4,"numerator":{"DistributePowers":[[{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Product":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]},{"Polynomial":{"poly":16,"rotation":-1}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Product":[{"Polynomial":{"poly":14,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":4,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":4,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Polynomial":{"poly":16,"rotation":-1}}]}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":5,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":5,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Negated":{"Negated":{"Polynomial":{"poly":15,"rotation":0}}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[6425625360762666998,7924344314350639699,14762033076929465436,2023505479389396574]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Negated":{"Polynomial":{"poly":16,"rotation":0}}}]}]},{"Product":[{"Product":[{"Product":[{"Polynomial":{"poly":6,"rotation":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Sum":[{"Constant":[415066004289224689,11886516471525959549,3696305541684646538,3035258219084094862]},{"Negated":{"Polynomial":{"poly":6,"rotation":0}}}]}]},{"Polynomial":{"poly":15,"rotation":0}}]},{"Product":[{"Polynomial":{"poly":7,"rotation":0}},{"Product":[{"Polynomial":{"poly":15,"rotation":0}},{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Negated":{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]}}]}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":19,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":-6}},{"Sum":[{"Product":[{"Polynomial":{"poly":20,"rotation":0}},{"Polynomial":{"poly":20,"rotation":0}}]},{"Negated":{"Polynomial":{"poly":20,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Polynomial":{"poly":20,"rotation":0}},{"Negated":{"Polynomial":{"poly":19,"rotation":-6}}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Polynomial":{"poly":19,"rotation":1}},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":8,"rotation":0}}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":9,"rotation":0}}]}]},{"Challenge":2}]}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":10,"rotation":0}}]}]},{"Challenge":2}]}]}]},{"Negated":{"Product":[{"Polynomial":{"poly":19,"rotation":0}},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":14,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":15,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[11100302345850292309,5109383341788583484,6450182039226333095,2498166472155664813]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":16,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[11922143911221101039,4762855335879493275,9634852812984583437,2104342265551292894]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]}]}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Polynomial":{"poly":20,"rotation":1}},{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":13,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":11,"rotation":0}}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":0,"rotation":0}},{"Product":[{"Challenge":1},{"Polynomial":{"poly":12,"rotation":0}}]}]},{"Challenge":2}]}]}]},{"Negated":{"Product":[{"Polynomial":{"poly":20,"rotation":0}},{"Product":[{"Sum":[{"Sum":[{"Polynomial":{"poly":13,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[6393076176221150738,7283021187728417300,15472188617747294665,3366061389777165561]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]},{"Sum":[{"Sum":[{"Polynomial":{"poly":0,"rotation":0}},{"Product":[{"Product":[{"Challenge":1},{"Constant":[5185504448716010194,13473122879869045206,7110214824824105482,296185565312886903]}]},{"CommonPolynomial":"Identity"}]}]},{"Challenge":2}]}]}]}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":21,"rotation":0}}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":-6}},{"Sum":[{"Product":[{"Polynomial":{"poly":21,"rotation":0}},{"Polynomial":{"poly":21,"rotation":0}}]},{"Negated":{"Polynomial":{"poly":21,"rotation":0}}}]}]},{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Product":[{"Product":[{"Polynomial":{"poly":21,"rotation":1}},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Challenge":1}]}]},{"Sum":[{"Polynomial":{"poly":18,"rotation":0}},{"Challenge":2}]}]},{"Negated":{"Product":[{"Product":[{"Polynomial":{"poly":21,"rotation":0}},{"Sum":[{"DistributePowers":[[{"Sum":[{"Product":[{"Polynomial":{"poly":3,"rotation":0}},{"Polynomial":{"poly":14,"rotation":0}}]},{"Scaled":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":3,"rotation":0}}}]},[0,0,0,0]]}]},{"Sum":[{"Product":[{"Polynomial":{"poly":3,"rotation":0}},{"Polynomial":{"poly":15,"rotation":0}}]},{"Scaled":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"Polynomial":{"poly":3,"rotation":0}}}]},[0,0,0,0]]}]}],{"Challenge":0}]},{"Challenge":1}]}]},{"Sum":[{"DistributePowers":[[{"Polynomial":{"poly":1,"rotation":0}},{"Polynomial":{"poly":2,"rotation":0}}],{"Challenge":0}]},{"Challenge":2}]}]}}]}]},{"Product":[{"CommonPolynomial":{"Lagrange":0}},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":18,"rotation":0}}}]}]},{"Product":[{"Product":[{"Sum":[{"Sum":[{"Constant":[12436184717236109307,3962172157175319849,7381016538464732718,1011752739694698287]},{"Negated":{"CommonPolynomial":{"Lagrange":-6}}}]},{"Negated":{"Sum":[{"Sum":[{"Sum":[{"Sum":[{"CommonPolynomial":{"Lagrange":-5}},{"CommonPolynomial":{"Lagrange":-4}}]},{"CommonPolynomial":{"Lagrange":-3}}]},{"CommonPolynomial":{"Lagrange":-2}}]},{"CommonPolynomial":{"Lagrange":-1}}]}}]},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":18,"rotation":0}}}]}]},{"Sum":[{"Polynomial":{"poly":17,"rotation":0}},{"Negated":{"Polynomial":{"poly":17,"rotation":-1}}}]}]}],{"Challenge":3}]}},"transcript_initial_state":[14020464927531827270,4059452293250824521,4573550151647582568,1227786991152106446],"instance_committing_key":null,"linearization":null,"accumulator_indices":[]}} \ No newline at end of file diff --git a/tests/wasm/test.provekey b/tests/wasm/test.provekey index 6b588eeec7dad7bc1bdaac2e133c6f191d0b18de..b23546bb6e644068b071fc368a1f99d936593220 100644 GIT binary patch delta 57925 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 G0t6u0Yj)ZI delta 57691 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 RiS#X`_5Iifw_LOW1Ry5@@?`)3