initialise

This commit is contained in:
gozzy 2022-08-28 18:48:50 +00:00
commit ef8ef3fc61
14 changed files with 2432 additions and 0 deletions

714
Cargo.lock generated Normal file
View File

@ -0,0 +1,714 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bellman_ce"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e1a2edf80a8ed042463f8888946f70fcd901f1615711bb253b7dc32b9c9fe73"
dependencies = [
"bit-vec",
"byteorder",
"cfg-if",
"crossbeam",
"futures",
"num_cpus",
"pairing_ce",
"rand",
]
[[package]]
name = "bit-vec"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4523a10839ffae575fb08aa3423026c8cb4687eef43952afb956229d4f246f7"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap-v3"
version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfac055d61c39ace5061621530f7f55651a261a4fba296ce1bad06d41a8de65e"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"clap_derive-v3",
"indexmap",
"lazy_static",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap_derive-v3"
version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6dd675567eb3e35787bd2583d129e85fabc7503b0a093d08c51198a307e2091"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "crossbeam"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
dependencies = [
"cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
dependencies = [
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
[[package]]
name = "exitcode"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
[[package]]
name = "ff_ce"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ced6646e4e98a53da162e38ffe9c865edbd7a2f9ff197067b0a8bf1114bf8a"
dependencies = [
"byteorder",
"ff_derive_ce",
"hex",
"rand",
]
[[package]]
name = "ff_derive_ce"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c052fa6d4c2f12305ec364bfb8ef884836f3f61ea015b202372ff996d1ac4b"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
[[package]]
name = "futures-executor"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
"num_cpus",
]
[[package]]
name = "futures-io"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
[[package]]
name = "futures-sink"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
[[package]]
name = "futures-task"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
[[package]]
name = "futures-util"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-utils",
"slab",
]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "hex-literal"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
dependencies = [
"hex-literal-impl",
"proc-macro-hack",
]
[[package]]
name = "hex-literal-impl"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
dependencies = [
"proc-macro-hack",
]
[[package]]
name = "indexmap"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
dependencies = [
"autocfg",
]
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memoffset"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
dependencies = [
"rustc_version",
]
[[package]]
name = "num-bigint"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "pairing_ce"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55ca3bd80245b5d43dd4467bc9ab5daf869bd76c6cd3ca54c4499b41923657d"
dependencies = [
"byteorder",
"ff_ce",
"rand",
]
[[package]]
name = "pin-utils"
version = "0.1.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
[[package]]
name = "proc-macro-error"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn-mid",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
[[package]]
name = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "strsim"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zkutil"
version = "0.5.0"
dependencies = [
"bellman_ce",
"byteorder",
"cfg-if",
"clap-v3",
"exitcode",
"hex-literal",
"itertools",
"num-bigint",
"num-traits",
"rand",
"serde",
"serde_json",
]

69
Cargo.toml Normal file
View File

@ -0,0 +1,69 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "zkutil"
version = "0.5.0"
authors = ["Roman Semenov <semenov.roma@gmail.com>"]
description = "Library for working with circom circuits"
homepage = "https://github.com/poma/zkutil"
documentation = "https://docs.rs/zkutil"
license = "MIT"
repository = "https://github.com/poma/zkutil"
[lib]
crate-type = ["cdylib", "lib"]
[[bin]]
name = "zkutil"
path = "src/main.rs"
[dependencies.bellman_ce]
version = "0.3.4"
default-features = false
[dependencies.byteorder]
version = "1"
[dependencies.cfg-if]
version = "0.1.10"
[dependencies.clap]
version = "3.0.0-beta.1"
package = "clap-v3"
[dependencies.exitcode]
version = "1.1.2"
[dependencies.hex-literal]
version = "0.2.1"
[dependencies.itertools]
version = "0.8.1"
[dependencies.num-bigint]
version = "0.2.3"
[dependencies.num-traits]
version = "0.2.8"
[dependencies.rand]
version = "0.4"
[dependencies.serde]
version = "1.0"
features = ["derive"]
[dependencies.serde_json]
version = "1.0"
[features]
default = ["bellman_ce/multicore"]

34
Cargo.toml.orig Normal file
View File

@ -0,0 +1,34 @@
[package]
name = "zkutil"
version = "0.5.0"
authors = ["Roman Semenov <semenov.roma@gmail.com>"]
description = "Library for working with circom circuits"
documentation = "https://docs.rs/zkutil"
homepage = "https://github.com/poma/zkutil"
license = "MIT"
repository = "https://github.com/poma/zkutil"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
[[bin]]
name = "zkutil"
path = "src/main.rs"
[dependencies]
rand = "0.4"
byteorder = "1"
exitcode = "1.1.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
num-bigint = "0.2.3"
num-traits = "0.2.8"
itertools = "0.8.1"
cfg-if = "0.1.10"
hex-literal = "0.2.1"
clap = { package = "clap-v3", version = "3.0.0-beta.1" } # todo: replace with official v3 when it's released to crates.io
bellman_ce = { version = "0.3.4", default-features = false } # active features depend on build type
[features]
default = ["bellman_ce/multicore"]

23
LICENSE.md Normal file
View File

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

124
README.md Normal file
View File

@ -0,0 +1,124 @@
# zkUtil ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/poma/zkutil/Rust) [![Crates.io](https://img.shields.io/crates/v/zkutil.svg)](https://crates.io/crates/zkutil)
A tool to work with zkSNARK circuits generated by circom compiler. Based on circom import code by @kobigurk
**The generated keys are currently incompatible with upstream Websnark and require using a custom fork**
## Usage examples:
```shell script
# Getting help
> zkutil --help
zkutil
A tool to work with SNARK circuits generated by circom
USAGE:
zkutil <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
export-keys Export proving and verifying keys compatible with snarkjs/websnark
generate-verifier Generate verifier smart contract
help Prints this message or the help of the given subcommand(s)
prove Generate a SNARK proof
setup Generate trusted setup parameters
verify Verify a SNARK proof
# Getting help for a subcommand
> zkutil prove --help
zkutil-prove
Generate a SNARK proof
USAGE:
zkutil prove [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-c, --circuit <circuit> Circuit R1CS or JSON file [default: circuit.r1cs]
-p, --params <params> Snark trusted setup parameters file [default: params.bin]
-r, --proof <proof> Output file for proof JSON [default: proof.json]
-i, --public <public> Output file for public inputs JSON [default: public.json]
-w, --witness <witness> Witness JSON file [default: witness.json]
# Suppose we have circuit file and a sample inputs
> ls
circuit.circom input.json
# Compile the circuit
> circom -rw circuit.circom
Constraints: 10000
Constraints: 20000
Constraints: 30000
Constraints: 40000
Constraints: 50000
# Generate a local trusted setup
> zkutil setup
Loading circuit...
Generating trusted setup parameters...
Has generated 28296 points
Writing to file...
Done!
# Calculate witness from the input.json
# At the moment we still need to calculate witness using snarkjs
> snarkjs calculatewitness
# Generate a snark proof
> zkutil prove
Loading circuit...
Proving...
Saved proof.json and public.json
# Verify the proof
> zkutil verify
Proof is correct
# Generate a solidity verifier contract
> zkutil generate-verifier
Created verifier.sol
# Export keys to snarkjs/websnark compatible format
> zkutil export-keys
Exporting params.bin...
Created proving_key.json and verification_key.json
# Verify the same proof with snarkjs
> snarkjs verify
OK
# Here's a list of files that we have after this
> ls
circuit.circom circuit.r1cs circuit.wasm input.json params.bin proof.json public.json Verifier.sol proving_key.json verifying_key.json witness.json
```
Also see `test.sh` for example
# Installation
Install Rust
```shell script
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
Install zkutil globally
```shell script
cargo install zkutil
# Make sure `~/.cargo/bin` is in $PATH (should be added automatically during Rust installation)
```
Or alternatively you can compile and run it instead:
```shell script
git clone https://github.com/poma/zkutil
cd zkutil
cargo run --release -- prove --help
```

15
circuit.circom Normal file
View File

@ -0,0 +1,15 @@
template Num2Bits(n) {
signal input in;
signal output out[n];
var lc1=0;
for (var i = 0; i<n; i++) {
out[i] <-- (in >> i) & 1;
out[i] * (out[i] -1 ) === 0;
lc1 += out[i] * 2**i;
}
lc1 === in;
}
component main = Num2Bits(8);

572
src/circom_circuit.rs Normal file
View File

@ -0,0 +1,572 @@
#![allow(clippy::needless_range_loop)]
extern crate bellman_ce;
extern crate rand;
use std::str;
use std::fs::{self, OpenOptions, File};
use std::io::{BufReader, Read, Seek};
use std::collections::BTreeMap;
use std::iter::repeat;
use std::sync::Arc;
use itertools::Itertools;
use rand::{Rng, OsRng};
use bellman_ce::{
Circuit,
SynthesisError,
Variable,
Index,
ConstraintSystem,
LinearCombination,
source::QueryDensity,
groth16::{
Parameters,
Proof,
generate_random_parameters as generate_random_parameters2,
prepare_verifying_key,
create_random_proof,
verify_proof,
prepare_prover,
},
pairing::{
Engine,
CurveAffine,
ff::PrimeField,
ff::ScalarEngine,
bn256::{
Bn256,
Fq,
Fq2,
G1Affine,
G2Affine,
}
}
};
use crate::utils::{
repr_to_big,
proof_to_hex,
p1_to_vec,
p2_to_vec,
pairing_to_vec,
};
#[derive(Serialize, Deserialize)]
struct CircuitJson {
pub constraints: Vec<Vec<BTreeMap<String, String>>>,
#[serde(rename = "nPubInputs")]
pub num_inputs: usize,
#[serde(rename = "nOutputs")]
pub num_outputs: usize,
#[serde(rename = "nVars")]
pub num_variables: usize,
}
#[derive(Serialize, Deserialize)]
struct ProofJson {
pub protocol: String,
pub proof: Option<String>,
pub pi_a: Vec<String>,
pub pi_b: Vec<Vec<String>>,
pub pi_c: Vec<String>,
}
#[derive(Serialize, Deserialize)]
struct ProvingKeyJson {
#[serde(rename = "polsA")]
pub pols_a: Vec<BTreeMap<String, String>>,
#[serde(rename = "polsB")]
pub pols_b: Vec<BTreeMap<String, String>>,
#[serde(rename = "polsC")]
pub pols_c: Vec<BTreeMap<String, String>>,
#[serde(rename = "A")]
pub a: Vec<Vec<String>>,
#[serde(rename = "B1")]
pub b1: Vec<Vec<String>>,
#[serde(rename = "B2")]
pub b2: Vec<Vec<Vec<String>>>,
#[serde(rename = "C")]
pub c: Vec<Option<Vec<String>>>,
pub vk_alfa_1: Vec<String>,
pub vk_beta_1: Vec<String>,
pub vk_delta_1: Vec<String>,
pub vk_beta_2: Vec<Vec<String>>,
pub vk_delta_2: Vec<Vec<String>>,
#[serde(rename = "hExps")]
pub h: Vec<Vec<String>>,
pub protocol: String,
#[serde(rename = "nPublic")]
pub n_public: usize,
#[serde(rename = "nVars")]
pub n_vars: usize,
#[serde(rename = "domainBits")]
pub domain_bits: usize,
#[serde(rename = "domainSize")]
pub domain_size: usize,
}
#[derive(Serialize, Deserialize)]
struct VerifyingKeyJson {
#[serde(rename = "IC")]
pub ic: Vec<Vec<String>>,
pub vk_alfa_1: Vec<String>,
pub vk_alpha_1: Vec<String>,
pub vk_beta_2: Vec<Vec<String>>,
pub vk_gamma_2: Vec<Vec<String>>,
pub vk_delta_2: Vec<Vec<String>>,
pub vk_alfabeta_12: Vec<Vec<Vec<String>>>,
pub vk_alphabeta_12: Vec<Vec<Vec<String>>>,
pub curve: String,
pub protocol: String,
#[serde(rename = "nPublic")]
pub inputs_count: usize,
}
pub type Constraint<E> = (
Vec<(usize, <E as ScalarEngine>::Fr)>,
Vec<(usize, <E as ScalarEngine>::Fr)>,
Vec<(usize, <E as ScalarEngine>::Fr)>,
);
#[derive(Clone)]
pub struct R1CS<E: Engine> {
pub num_inputs: usize,
pub num_aux: usize,
pub num_variables: usize,
pub constraints: Vec<Constraint<E>>,
}
#[derive(Clone)]
pub struct CircomCircuit<E: Engine> {
pub r1cs: R1CS<E>,
pub witness: Option<Vec<E::Fr>>,
pub wire_mapping: Option<Vec<usize>>,
// debug symbols
}
impl<'a, E: Engine> CircomCircuit<E> {
pub fn get_public_inputs(&self) -> Option<Vec<E::Fr>> {
match &self.witness {
None => None,
Some(w) => match &self.wire_mapping {
None => Some(w[1..self.r1cs.num_inputs].to_vec()),
Some(m) => Some(m[1..self.r1cs.num_inputs].iter().map(|i| w[*i]).collect_vec()),
}
}
}
pub fn get_public_inputs_json(&self) -> String {
let inputs = self.get_public_inputs();
let inputs = match inputs {
None => return String::from("[]"),
Some(inp) => inp.iter().map(|x| repr_to_big(x.into_repr())).collect_vec(),
};
serde_json::to_string_pretty(&inputs).unwrap()
}
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for CircomCircuit<E> {
//noinspection RsBorrowChecker
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let witness = &self.witness;
let wire_mapping = &self.wire_mapping;
for i in 1..self.r1cs.num_inputs {
cs.alloc_input(
|| format!("variable {}", i),
|| {
Ok(match witness {
None => E::Fr::from_str("1").unwrap(),
Some(w) => match wire_mapping {
None => w[i],
Some(m) => w[m[i]],
}
})
},
)?;
}
for i in 0..self.r1cs.num_aux {
cs.alloc(
|| format!("aux {}", i),
|| {
Ok(match witness {
None => E::Fr::from_str("1").unwrap(),
Some(w) => match wire_mapping {
None => w[i + self.r1cs.num_inputs],
Some(m) => w[m[i + self.r1cs.num_inputs]],
},
})
},
)?;
}
let make_index = |index|
if index < self.r1cs.num_inputs {
Index::Input(index)
} else {
Index::Aux(index - self.r1cs.num_inputs)
};
let make_lc = |lc_data: Vec<(usize, E::Fr)>|
lc_data.iter().fold(
LinearCombination::<E>::zero(),
|lc: LinearCombination<E>, (index, coeff)| lc + (*coeff, Variable::new_unchecked(make_index(*index)))
);
for (i, constraint) in self.r1cs.constraints.iter().enumerate() {
cs.enforce(|| format!("constraint {}", i),
|_| make_lc(constraint.0.clone()),
|_| make_lc(constraint.1.clone()),
|_| make_lc(constraint.2.clone()),
);
}
Ok(())
}
}
pub fn prove<E: Engine, R: Rng>(circuit: CircomCircuit<E>, params: &Parameters<E>, mut rng: R) -> Result<Proof<E>, SynthesisError> {
let mut params2 = params.clone();
filter_params(&mut params2);
create_random_proof(circuit, &params2, &mut rng)
}
pub fn generate_random_parameters<E: Engine, R: Rng>(circuit: CircomCircuit<E>, mut rng: R) -> Result<Parameters<E>, SynthesisError> {
generate_random_parameters2(circuit, &mut rng)
}
pub fn verify_circuit<E: Engine>(circuit: &CircomCircuit<E>, params: &Parameters<E>, proof: &Proof<E>) -> Result<bool, SynthesisError> {
let inputs = match circuit.get_public_inputs() {
None => return Err(SynthesisError::AssignmentMissing),
Some(inp) => inp,
};
verify_proof(&prepare_verifying_key(&params.vk), proof, &inputs)
}
pub fn verify<E: Engine>(params: &Parameters<E>, proof: &Proof<E>, inputs: &[E::Fr]) -> Result<bool, SynthesisError> {
verify_proof(&prepare_verifying_key(&params.vk), proof, &inputs)
}
pub fn create_verifier_sol(params: &Parameters<Bn256>) -> String {
// TODO: use a simple template engine
let bytes = include_bytes!("verifier_groth.sol");
let template = String::from_utf8_lossy(bytes);
let p1_to_str = |p: &<Bn256 as Engine>::G1Affine| {
if p.is_zero() {
// todo: throw instead
return String::from("<POINT_AT_INFINITY>");
}
let xy = p.into_xy_unchecked();
let x = repr_to_big(xy.0.into_repr());
let y = repr_to_big(xy.1.into_repr());
format!("uint256({}), uint256({})", x, y)
};
let p2_to_str = |p: &<Bn256 as Engine>::G2Affine| {
if p.is_zero() {
// todo: throw instead
return String::from("<POINT_AT_INFINITY>");
}
let xy = p.into_xy_unchecked();
let x_c0 = repr_to_big(xy.0.c0.into_repr());
let x_c1 = repr_to_big(xy.0.c1.into_repr());
let y_c0 = repr_to_big(xy.1.c0.into_repr());
let y_c1 = repr_to_big(xy.1.c1.into_repr());
format!("[uint256({}), uint256({})], [uint256({}), uint256({})]", x_c1, x_c0, y_c1, y_c0)
};
let template = template.replace("<%vk_alfa1%>", &*p1_to_str(&params.vk.alpha_g1));
let template = template.replace("<%vk_beta2%>", &*p2_to_str(&params.vk.beta_g2));
let template = template.replace("<%vk_gamma2%>", &*p2_to_str(&params.vk.gamma_g2));
let template = template.replace("<%vk_delta2%>", &*p2_to_str(&params.vk.delta_g2));
let template = template.replace("<%vk_ic_length%>", &*params.vk.ic.len().to_string());
let template = template.replace("<%vk_input_length%>", &*(params.vk.ic.len() - 1).to_string());
let mut vi = String::from("");
for i in 0..params.vk.ic.len() {
vi = format!("{}{}vk.IC[{}] = Pairing.G1Point({});\n", vi, if vi.is_empty() { "" } else { " " }, i, &*p1_to_str(&params.vk.ic[i]));
}
template.replace("<%vk_ic_pts%>", &*vi)
}
pub fn create_verifier_sol_file(params: &Parameters<Bn256>, filename: &str) -> std::io::Result<()> {
fs::write(filename, create_verifier_sol(params).as_bytes())
}
pub fn proof_to_json(proof: &Proof<Bn256>) -> Result<String, serde_json::error::Error> {
serde_json::to_string_pretty(&ProofJson {
protocol: "groth".to_string(),
proof: Some(proof_to_hex(&proof)),
pi_a: p1_to_vec(&proof.a),
pi_b: p2_to_vec(&proof.b),
pi_c: p1_to_vec(&proof.c),
})
}
pub fn proof_to_json_file(proof: &Proof<Bn256>, filename: &str) -> std::io::Result<()> {
let str = proof_to_json(proof).unwrap(); // TODO: proper error handling
fs::write(filename, str.as_bytes())
}
pub fn load_params_file(filename: &str) -> Parameters<Bn256> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
load_params(reader)
}
pub fn load_params<R: Read>(reader: R) -> Parameters<Bn256> {
Parameters::read(reader, true).expect("unable to read params")
}
pub fn load_inputs_json_file<E: Engine>(filename: &str) -> Vec<E::Fr> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
load_inputs_json::<E, BufReader<File>>(BufReader::new(reader))
}
pub fn load_inputs_json<E: Engine, R: Read>(reader: R) -> Vec<E::Fr> {
let inputs: Vec<String> = serde_json::from_reader(reader).unwrap();
inputs.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::<Vec<E::Fr>>()
}
pub fn load_proof_json_file<E: Engine>(filename: &str) -> Proof<Bn256> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
load_proof_json(BufReader::new(reader))
}
pub fn load_proof_json<R: Read>(reader: R) -> Proof<Bn256> {
let proof: ProofJson = serde_json::from_reader(reader).unwrap();
Proof {
a: G1Affine::from_xy_checked(
Fq::from_str(&proof.pi_a[0]).unwrap(),
Fq::from_str(&proof.pi_a[1]).unwrap(),
).unwrap(),
b: G2Affine::from_xy_checked(
Fq2 {
c0: Fq::from_str(&proof.pi_b[0][0]).unwrap(),
c1: Fq::from_str(&proof.pi_b[0][1]).unwrap(),
},
Fq2 {
c0: Fq::from_str(&proof.pi_b[1][0]).unwrap(),
c1: Fq::from_str(&proof.pi_b[1][1]).unwrap(),
},
).unwrap(),
c: G1Affine::from_xy_checked(
Fq::from_str(&proof.pi_c[0]).unwrap(),
Fq::from_str(&proof.pi_c[1]).unwrap(),
).unwrap(),
}
}
pub fn filter_params<E: Engine>(params: &mut Parameters<E>) {
params.vk.ic = params.vk.ic.clone().into_iter().filter(|x| !x.is_zero()).collect::<Vec<_>>();
params.h = Arc::new((*params.h).clone().into_iter().filter(|x| !x.is_zero()).collect::<Vec<_>>());
params.a = Arc::new((*params.a).clone().into_iter().filter(|x| !x.is_zero()).collect::<Vec<_>>());
params.b_g1 = Arc::new((*params.b_g1).clone().into_iter().filter(|x| !x.is_zero()).collect::<Vec<_>>());
params.b_g2 = Arc::new((*params.b_g2).clone().into_iter().filter(|x| !x.is_zero()).collect::<Vec<_>>());
}
pub fn proving_key_json(params: &Parameters<Bn256>, circuit: CircomCircuit<Bn256>) -> Result<String, serde_json::error::Error> {
let mut pols_a: Vec<BTreeMap<String, String>> = vec![];
let mut pols_b: Vec<BTreeMap<String, String>> = vec![];
let mut pols_c: Vec<BTreeMap<String, String>> = vec![];
for _ in 0..circuit.r1cs.num_aux + circuit.r1cs.num_inputs {
pols_a.push(BTreeMap::new());
pols_b.push(BTreeMap::new());
pols_c.push(BTreeMap::new());
}
for c in 0..circuit.r1cs.constraints.len() {
for item in circuit.r1cs.constraints[c].0.iter() {
pols_a[item.0].insert(c.to_string(), repr_to_big(item.1.into_repr()));
}
for item in circuit.r1cs.constraints[c].1.iter() {
pols_b[item.0].insert(c.to_string(), repr_to_big(item.1.into_repr()));
}
for item in circuit.r1cs.constraints[c].2.iter() {
pols_c[item.0].insert(c.to_string(), repr_to_big(item.1.into_repr()));
}
}
for i in 0..circuit.r1cs.num_inputs {
pols_a[i].insert((circuit.r1cs.constraints.len() + i).to_string(), String::from("1"));
}
let domain_bits = log2_floor(circuit.r1cs.constraints.len() + circuit.r1cs.num_inputs) + 1;
let n_public = circuit.r1cs.num_inputs - 1;
let n_vars = circuit.r1cs.num_variables;
let p = prepare_prover(circuit).unwrap().assignment;
let mut a_iter = params.a.iter();
let mut b1_iter = params.b_g1.iter();
let mut b2_iter = params.b_g2.iter();
let zero1 = G1Affine::zero();
let zero2 = G2Affine::zero();
let a = repeat(true).take(params.vk.ic.len())
.chain(p.a_aux_density.iter())
.map(|item| if item { a_iter.next().unwrap() } else { &zero1 })
.map(|e| p1_to_vec(e))
.collect_vec();
let b1 = p.b_input_density.iter()
.chain(p.b_aux_density.iter())
.map(|item| if item { b1_iter.next().unwrap() } else { &zero1 })
.map(|e| p1_to_vec(e))
.collect_vec();
let b2 = p.b_input_density.iter()
.chain(p.b_aux_density.iter())
.map(|item| if item { b2_iter.next().unwrap() } else { &zero2 })
.map(|e| p2_to_vec(e))
.collect_vec();
let c = repeat(None).take(params.vk.ic.len())
.chain(params.l.iter().map(|e| Some(p1_to_vec(e))))
.collect_vec();
let proving_key = ProvingKeyJson {
pols_a,
pols_b,
pols_c,
a,
b1,
b2,
c,
vk_alfa_1: p1_to_vec(&params.vk.alpha_g1),
vk_beta_1: p1_to_vec(&params.vk.beta_g1),
vk_delta_1: p1_to_vec(&params.vk.delta_g1),
vk_beta_2: p2_to_vec(&params.vk.beta_g2),
vk_delta_2: p2_to_vec(&params.vk.delta_g2),
h: params.h.iter().map(|e| p1_to_vec(e)).collect_vec(),
protocol: String::from("groth"),
n_public,
n_vars,
domain_bits,
domain_size: 1 << domain_bits,
};
serde_json::to_string(&proving_key)
}
fn log2_floor(num: usize) -> usize {
assert!(num > 0);
let mut pow = 0;
while (1 << (pow + 1)) <= num {
pow += 1;
}
pow
}
pub fn proving_key_json_file(params: &Parameters<Bn256>, circuit: CircomCircuit<Bn256>, filename: &str) -> std::io::Result<()> {
let str = proving_key_json(params, circuit).unwrap(); // TODO: proper error handling
fs::write(filename, str.as_bytes())
}
pub fn verification_key_json(params: &Parameters<Bn256>) -> Result<String, serde_json::error::Error> {
let verification_key = VerifyingKeyJson {
ic: params.vk.ic.iter().map(|e| p1_to_vec(e)).collect_vec(),
vk_alfa_1: p1_to_vec(&params.vk.alpha_g1),
vk_alpha_1: p1_to_vec(&params.vk.alpha_g1),
vk_beta_2: p2_to_vec(&params.vk.beta_g2),
vk_gamma_2: p2_to_vec(&params.vk.gamma_g2),
vk_delta_2: p2_to_vec(&params.vk.delta_g2),
vk_alfabeta_12: pairing_to_vec(&Bn256::pairing(params.vk.alpha_g1, params.vk.beta_g2)),
vk_alphabeta_12: pairing_to_vec(&Bn256::pairing(params.vk.alpha_g1, params.vk.beta_g2)),
inputs_count: params.vk.ic.len() - 1,
curve: String::from("BN254"),
protocol: String::from("groth"),
};
serde_json::to_string_pretty(&verification_key)
}
pub fn verification_key_json_file(params: &Parameters<Bn256>, filename: &str) -> std::io::Result<()> {
let str = verification_key_json(params).unwrap(); // TODO: proper error handling
fs::write(filename, str.as_bytes())
}
pub fn witness_from_json_file<E: Engine>(filename: &str) -> Vec<E::Fr> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
witness_from_json::<E, BufReader<File>>(BufReader::new(reader))
}
pub fn witness_from_json<E: Engine, R: Read>(reader: R) -> Vec<E::Fr> {
let witness: Vec<String> = serde_json::from_reader(reader).unwrap();
witness.into_iter().map(|x| E::Fr::from_str(&x).unwrap()).collect::<Vec<E::Fr>>()
}
pub fn witness_from_bin_file<E: Engine>(filename: &str) -> Result<Vec<E::Fr>, std::io::Error> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
witness_from_bin::<E, BufReader<File>>(BufReader::new(reader))
}
pub fn witness_from_bin<E: Engine, R: Read>(reader: R) -> Result<Vec<E::Fr>, std::io::Error> {
let file = crate::wtns_reader::read::<E, R>(reader)?;
Ok(file.witness)
}
pub fn r1cs_from_json_file<E: Engine>(filename: &str) -> R1CS<E> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
r1cs_from_json(BufReader::new(reader))
}
pub fn r1cs_from_json<E: Engine, R: Read>(reader: R) -> R1CS<E> {
let circuit_json: CircuitJson = serde_json::from_reader(reader).unwrap();
let num_inputs = circuit_json.num_inputs + circuit_json.num_outputs + 1;
let num_aux = circuit_json.num_variables - num_inputs;
let convert_constraint = |lc: &BTreeMap<String, String>| {
lc.iter().map(|(index, coeff)| (index.parse().unwrap(), E::Fr::from_str(coeff).unwrap())).collect_vec()
};
let constraints = circuit_json.constraints.iter().map(
|c| (convert_constraint(&c[0]), convert_constraint(&c[1]), convert_constraint(&c[2]))
).collect_vec();
R1CS {
num_inputs,
num_aux,
num_variables: circuit_json.num_variables,
constraints,
}
}
pub fn r1cs_from_bin<R: Read + Seek>(reader: R) -> Result<(R1CS<Bn256>, Vec<usize>), std::io::Error> {
let file = crate::r1cs_reader::read(reader)?;
let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize;
let num_variables = file.header.n_wires as usize;
let num_aux = num_variables - num_inputs;
Ok((
R1CS { num_aux, num_inputs, num_variables, constraints: file.constraints, },
file.wire_mapping.iter().map(|e| *e as usize).collect_vec()
))
}
pub fn r1cs_from_bin_file(filename: &str) -> Result<(R1CS<Bn256>, Vec<usize>), std::io::Error> {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
r1cs_from_bin(BufReader::new(reader))
}
pub fn create_rng() -> Box<dyn Rng> {
Box::new(OsRng::new().unwrap())
}

15
src/lib.rs Normal file
View File

@ -0,0 +1,15 @@
#[macro_use]
extern crate serde;
#[macro_use]
extern crate hex_literal;
extern crate bellman_ce;
extern crate rand;
extern crate itertools;
extern crate byteorder;
extern crate num_bigint;
extern crate num_traits;
pub mod utils;
pub mod circom_circuit;
pub mod r1cs_reader;
pub mod wtns_reader;

253
src/main.rs Normal file
View File

@ -0,0 +1,253 @@
extern crate clap;
extern crate bellman_ce;
extern crate zkutil;
use std::fs;
use std::fs::File;
use std::path::Path;
use clap::Clap;
use bellman_ce::pairing::{
Engine,
bn256::Bn256
};
use zkutil::circom_circuit::{
prove as prove2,
verify as verify2,
create_rng,
load_params_file,
proof_to_json_file,
r1cs_from_json_file,
r1cs_from_bin_file,
witness_from_json_file,
witness_from_bin_file,
load_proof_json_file,
load_inputs_json_file,
create_verifier_sol_file,
proving_key_json_file,
verification_key_json_file,
generate_random_parameters,
CircomCircuit,
R1CS,
};
/// A tool to work with SNARK circuits generated by circom
#[derive(Clap)]
struct Opts {
#[clap(subcommand)]
command: SubCommand,
}
#[derive(Clap)]
enum SubCommand {
/// Generate a SNARK proof
Prove(ProveOpts),
/// Verify a SNARK proof
Verify(VerifyOpts),
/// Generate trusted setup parameters
Setup(SetupOpts),
/// Generate verifier smart contract
GenerateVerifier(GenerateVerifierOpts),
/// Export proving and verifying keys compatible with snarkjs/websnark
ExportKeys(ExportKeysOpts),
}
/// A subcommand for generating a SNARK proof
#[derive(Clap)]
struct ProveOpts {
/// Snark trusted setup parameters file
#[clap(short = "p", long = "params", default_value = "params.bin")]
params: String,
/// Circuit R1CS or JSON file [default: circuit.r1cs|circuit.json]
#[clap(short = "c", long = "circuit")]
circuit: Option<String>,
/// Witness JSON file [default: witness.wtns|witness.json]
#[clap(short = "w", long = "witness")]
witness: Option<String>,
/// Output file for proof JSON
#[clap(short = "r", long = "proof", default_value = "proof.json")]
proof: String,
/// Output file for public inputs JSON
#[clap(short = "o", long = "public", default_value = "public.json")]
public: String,
}
/// A subcommand for verifying a SNARK proof
#[derive(Clap)]
struct VerifyOpts {
/// Snark trusted setup parameters file
#[clap(short = "p", long = "params", default_value = "params.bin")]
params: String,
/// Proof JSON file
#[clap(short = "r", long = "proof", default_value = "proof.json")]
proof: String,
/// Public inputs JSON file
#[clap(short = "i", long = "public", default_value = "public.json")]
public: String,
}
/// A subcommand for generating a trusted setup parameters
#[derive(Clap)]
struct SetupOpts {
/// Snark trusted setup parameters file
#[clap(short = "p", long = "params", default_value = "params.bin")]
params: String,
/// Circuit R1CS or JSON file [default: circuit.r1cs|circuit.json]
#[clap(short = "c", long = "circuit")]
circuit: Option<String>,
}
/// A subcommand for generating a Solidity verifier smart contract
#[derive(Clap)]
struct GenerateVerifierOpts {
/// Snark trusted setup parameters file
#[clap(short = "p", long = "params", default_value = "params.bin")]
params: String,
/// Output smart contract name
#[clap(short = "v", long = "verifier", default_value = "Verifier.sol")]
verifier: String,
}
/// A subcommand for exporting proving and verifying keys compatible with snarkjs/websnark
#[derive(Clap)]
struct ExportKeysOpts {
/// Snark trusted setup parameters file
#[clap(short = "p", long = "params", default_value = "params.bin")]
params: String,
/// Circuit R1CS or JSON file [default: circuit.r1cs|circuit.json]
#[clap(short = "c", long = "circuit")]
circuit: Option<String>,
/// Output proving key file
#[clap(short = "r", long = "pk", default_value = "proving_key.json")]
pk: String,
/// Output verifying key file
#[clap(short = "v", long = "vk", default_value = "verification_key.json")]
vk: String,
}
fn main() {
let opts: Opts = Opts::parse();
match opts.command {
SubCommand::Prove(o) => {
prove(o);
}
SubCommand::Verify(o) => {
verify(o);
}
SubCommand::Setup(o) => {
setup(o);
}
SubCommand::GenerateVerifier(o) => {
generate_verifier(o);
}
SubCommand::ExportKeys(o) => {
export_keys(o);
}
}
}
fn load_r1cs(filename: &str) -> R1CS<Bn256> {
if filename.ends_with("json") {
r1cs_from_json_file(filename)
} else {
let (r1cs, _wire_mapping) = r1cs_from_bin_file(filename).unwrap();
r1cs
}
}
fn resolve_circuit_file(filename: Option<String>) -> String {
match filename {
Some(s) => s,
None => if Path::new("circuit.r1cs").exists() || !Path::new("circuit.json").exists() {
"circuit.r1cs".to_string()
} else {
"circuit.json".to_string()
}
}
}
fn load_witness<E: Engine>(filename: &str) -> Vec<E::Fr> {
if filename.ends_with("json") {
witness_from_json_file::<E>(filename)
} else {
witness_from_bin_file::<E>(filename).unwrap()
}
}
fn resolve_witness_file(filename: Option<String>) -> String {
match filename {
Some(s) => s,
None => if Path::new("witness.wtns").exists() || !Path::new("witness.json").exists() {
"witness.wtns".to_string()
} else {
"witness.json".to_string()
}
}
}
fn prove(opts: ProveOpts) {
let rng = create_rng();
let params = load_params_file(&opts.params);
let circuit_file = resolve_circuit_file(opts.circuit);
let witness_file = resolve_witness_file(opts.witness);
println!("Loading circuit from {}...", circuit_file);
let circuit = CircomCircuit {
r1cs: load_r1cs(&circuit_file),
witness: Some(load_witness::<Bn256>(&witness_file)),
wire_mapping: None,
};
println!("Proving...");
let proof = prove2(circuit.clone(), &params, rng).unwrap();
proof_to_json_file(&proof, &opts.proof).unwrap();
fs::write(&opts.public, circuit.get_public_inputs_json().as_bytes()).unwrap();
println!("Saved {} and {}", opts.proof, opts.public);
}
fn verify(opts: VerifyOpts) {
let params = load_params_file(&opts.params);
let proof = load_proof_json_file::<Bn256>(&opts.proof);
let inputs = load_inputs_json_file::<Bn256>(&opts.public);
let correct = verify2(&params, &proof, &inputs).unwrap();
if correct {
println!("Proof is correct");
} else {
println!("Proof is invalid!");
std::process::exit(400);
}
}
fn setup(opts: SetupOpts) {
let circuit_file = resolve_circuit_file(opts.circuit);
println!("Loading circuit from {}...", circuit_file);
let rng = create_rng();
let circuit = CircomCircuit {
r1cs: load_r1cs(&circuit_file),
witness: None,
wire_mapping: None,
};
println!("Generating trusted setup parameters...");
let params = generate_random_parameters(circuit, rng).unwrap();
println!("Writing to file...");
let writer = File::create(&opts.params).unwrap();
params.write(writer).unwrap();
println!("Saved parameters to {}", opts.params);
}
fn generate_verifier(opts: GenerateVerifierOpts) {
let params = load_params_file(&opts.params);
create_verifier_sol_file(&params, &opts.verifier).unwrap();
println!("Created {}", opts.verifier);
}
fn export_keys(opts: ExportKeysOpts) {
println!("Exporting {}...", opts.params);
let params = load_params_file(&opts.params);
let circuit_file = resolve_circuit_file(opts.circuit);
let circuit = CircomCircuit {
r1cs: load_r1cs(&circuit_file),
witness: None,
wire_mapping: None,
};
proving_key_json_file(&params, circuit, &opts.pk).unwrap();
verification_key_json_file(&params, &opts.vk).unwrap();
println!("Created {} and {}.", opts.pk, opts.vk);
}

227
src/r1cs_reader.rs Normal file
View File

@ -0,0 +1,227 @@
#![allow(unused_variables, dead_code)]
use byteorder::{ReadBytesExt, LittleEndian};
use std::{collections::HashMap, io::{Error, ErrorKind, Read, Result, Seek, SeekFrom}};
use bellman_ce::pairing::{
Engine,
bn256::Bn256,
ff::{
Field, PrimeField, PrimeFieldRepr,
}
};
use crate::circom_circuit::Constraint;
#[cfg(test)]
use std::io::{BufReader, Cursor};
pub struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub n_wires: u32,
pub n_pub_out: u32,
pub n_pub_in: u32,
pub n_prv_in: u32,
pub n_labels: u64,
pub n_constraints: u32,
}
pub struct R1CSFile<E: Engine> {
pub version: u32,
pub header: Header,
pub constraints: Vec<Constraint<E>>,
pub wire_mapping: Vec<u64>,
}
fn read_field<R: Read, E: Engine>(mut reader: R) -> Result<E::Fr> {
let mut repr = E::Fr::zero().into_repr();
repr.read_le(&mut reader)?;
let fr = E::Fr::from_repr(repr)
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
Ok(fr)
}
fn read_header<R: Read>(mut reader: R, size: u64) -> Result<Header> {
let field_size = reader.read_u32::<LittleEndian>()?;
let mut prime_size = vec![0u8; field_size as usize];
reader.read_exact(&mut prime_size)?;
if size != 32 + field_size as u64 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid header section size"))
}
Ok(Header {
field_size,
prime_size,
n_wires: reader.read_u32::<LittleEndian>()?,
n_pub_out: reader.read_u32::<LittleEndian>()?,
n_pub_in: reader.read_u32::<LittleEndian>()?,
n_prv_in: reader.read_u32::<LittleEndian>()?,
n_labels: reader.read_u64::<LittleEndian>()?,
n_constraints: reader.read_u32::<LittleEndian>()?,
})
}
fn read_constraint_vec<R: Read, E:Engine>(mut reader: R, header: &Header) -> Result<Vec<(usize, E::Fr)>> {
let n_vec = reader.read_u32::<LittleEndian>()? as usize;
let mut vec = Vec::with_capacity(n_vec);
for _ in 0..n_vec {
vec.push((
reader.read_u32::<LittleEndian>()? as usize,
read_field::<&mut R, E>(&mut reader)?,
));
}
Ok(vec)
}
fn read_constraints<R: Read, E: Engine>(mut reader: R, size: u64, header: &Header) -> Result<Vec<Constraint<E>>> {
// todo check section size
let mut vec = Vec::with_capacity(header.n_constraints as usize);
for _ in 0..header.n_constraints {
vec.push((
read_constraint_vec::<&mut R, E>(&mut reader, &header)?,
read_constraint_vec::<&mut R, E>(&mut reader, &header)?,
read_constraint_vec::<&mut R, E>(&mut reader, &header)?,
));
}
Ok(vec)
}
fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> Result<Vec<u64>> {
if size != header.n_wires as u64 * 8 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid map section size"))
}
let mut vec = Vec::with_capacity(header.n_wires as usize);
for _ in 0..header.n_wires {
vec.push(reader.read_u64::<LittleEndian>()?);
}
if vec[0] != 0 {
return Err(Error::new(ErrorKind::InvalidData, "Wire 0 should always be mapped to 0"))
}
Ok(vec)
}
pub fn read<R: Read + Seek>(mut reader: R) -> Result<R1CSFile<Bn256>> {
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
if magic != [0x72, 0x31, 0x63, 0x73] { // magic = "r1cs"
return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number"))
}
let version = reader.read_u32::<LittleEndian>()?;
if version != 1 {
return Err(Error::new(ErrorKind::InvalidData, "Unsupported version"))
}
let num_sections = reader.read_u32::<LittleEndian>()?;
// section type -> file offset
let mut sec_offsets = HashMap::<u32, u64>::new();
let mut sec_sizes = HashMap::<u32, u64>::new();
// get file offset of each section
for _ in 0..num_sections {
let sec_type = reader.read_u32::<LittleEndian>()?;
let sec_size = reader.read_u64::<LittleEndian>()?;
let offset = reader.seek(SeekFrom::Current(0))?;
sec_offsets.insert(sec_type, offset);
sec_sizes.insert(sec_type, sec_size);
reader.seek(SeekFrom::Current(sec_size as i64))?;
}
let header_type = 1;
let constraint_type = 2;
let wire2label_type = 3;
reader.seek(SeekFrom::Start(*sec_offsets.get(&header_type).unwrap()))?;
let header = read_header(&mut reader, *sec_sizes.get(&header_type).unwrap())?;
if header.field_size != 32 {
return Err(Error::new(ErrorKind::InvalidData, "This parser only supports 32-byte fields"))
}
if header.prime_size != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") {
return Err(Error::new(ErrorKind::InvalidData, "This parser only supports bn256"))
}
reader.seek(SeekFrom::Start(*sec_offsets.get(&constraint_type).unwrap()))?;
let constraints = read_constraints::<&mut R, Bn256>(&mut reader, *sec_sizes.get(&constraint_type).unwrap(), &header)?;
reader.seek(SeekFrom::Start(*sec_offsets.get(&wire2label_type).unwrap()))?;
let wire_mapping = read_map(&mut reader, *sec_sizes.get(&wire2label_type).unwrap(), &header)?;
Ok(R1CSFile { version, header, constraints, wire_mapping })
}
#[test]
fn sample() {
let data = hex!("
72316373
01000000
03000000
01000000 40000000 00000000
20000000
010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
07000000
01000000
02000000
03000000
e8030000 00000000
03000000
02000000 88020000 00000000
02000000
05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
06000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000
00000000 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000000 14000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000000
00000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000000 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000
01000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
04000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000000
03000000 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
06000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
01000000
06000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000
00000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000000 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
01000000
06000000 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
03000000 38000000 00000000
00000000 00000000
03000000 00000000
0a000000 00000000
0b000000 00000000
0c000000 00000000
0f000000 00000000
44010000 00000000
");
use bellman_ce::pairing::ff;
let reader = BufReader::new(Cursor::new(&data[..]));
let file = read(reader).unwrap();
assert_eq!(file.version, 1);
assert_eq!(file.header.field_size, 32);
assert_eq!(file.header.prime_size, &hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430"));
assert_eq!(file.header.n_wires, 7);
assert_eq!(file.header.n_pub_out, 1);
assert_eq!(file.header.n_pub_in, 2);
assert_eq!(file.header.n_prv_in, 3);
assert_eq!(file.header.n_labels, 0x03e8);
assert_eq!(file.header.n_constraints, 3);
assert_eq!(file.constraints.len(), 3);
assert_eq!(file.constraints[0].0.len(), 2);
assert_eq!(file.constraints[0].0[0].0, 5);
assert_eq!(file.constraints[0].0[0].1, ff::from_hex("0x03").unwrap());
assert_eq!(file.constraints[2].1[0].0, 0);
assert_eq!(file.constraints[2].1[0].1, ff::from_hex("0x06").unwrap());
assert_eq!(file.constraints[1].2.len(), 0);
assert_eq!(file.wire_mapping.len(), 7);
assert_eq!(file.wire_mapping[1], 3);
}

102
src/utils.rs Normal file
View File

@ -0,0 +1,102 @@
extern crate bellman_ce;
extern crate rand;
extern crate byteorder;
extern crate num_bigint;
extern crate num_traits;
use std::fmt::Display;
use itertools::Itertools;
use num_bigint::BigUint;
use num_traits::Num;
use bellman_ce::{
groth16::Proof,
pairing::{
ff::PrimeField,
CurveAffine,
bn256::{
G1Affine,
G2Affine,
Fq12,
Bn256,
},
},
};
pub fn repr_to_big<T: Display>(r: T) -> String {
BigUint::from_str_radix(&format!("{}", r)[2..], 16).unwrap().to_str_radix(10)
}
pub fn repr_to_hex<T: Display>(r: T) -> String {
format!("{}", r)[2..].to_string()
}
pub fn proof_to_hex(proof: &Proof<Bn256>) -> String {
let a = proof.a.into_xy_unchecked();
let b = proof.b.into_xy_unchecked();
let c = proof.c.into_xy_unchecked();
[a.0, a.1, b.0.c1, b.0.c0, b.1.c1, b.1.c0, c.0, c.1]
.iter()
.map(|e| repr_to_hex(e.into_repr()))
.join("")
}
pub fn p1_to_vec(p: &G1Affine) -> Vec<String> {
let xy = p.into_xy_unchecked();
vec![
repr_to_big(xy.0.into_repr()),
repr_to_big(xy.1.into_repr()),
if p.is_zero() { "0".to_string() } else { "1".to_string() }
]
}
pub fn p2_to_vec(p: &G2Affine) -> Vec<Vec<String>> {
let xy = p.into_xy_unchecked();
vec![
vec![
repr_to_big(xy.0.c0.into_repr()),
repr_to_big(xy.0.c1.into_repr()),
],
vec![
repr_to_big(xy.1.c0.into_repr()),
repr_to_big(xy.1.c1.into_repr()),
],
if p.is_zero() {
vec!["0".to_string(), "0".to_string()]
} else {
vec!["1".to_string(), "0".to_string()]
},
]
}
pub fn pairing_to_vec(p: &Fq12) -> Vec<Vec<Vec<String>>> {
vec![
vec![
vec![
repr_to_big(p.c0.c0.c0.into_repr()),
repr_to_big(p.c0.c0.c1.into_repr()),
],
vec![
repr_to_big(p.c0.c1.c0.into_repr()),
repr_to_big(p.c0.c1.c1.into_repr()),
],
vec![
repr_to_big(p.c0.c2.c0.into_repr()),
repr_to_big(p.c0.c2.c1.into_repr()),
],
],
vec![
vec![
repr_to_big(p.c1.c0.c0.into_repr()),
repr_to_big(p.c1.c0.c1.into_repr()),
],
vec![
repr_to_big(p.c1.c1.c0.into_repr()),
repr_to_big(p.c1.c1.c1.into_repr()),
],
vec![
repr_to_big(p.c1.c2.c0.into_repr()),
repr_to_big(p.c1.c2.c1.into_repr()),
],
],
]
}

168
src/verifier_groth.sol Normal file
View File

@ -0,0 +1,168 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
library Pairing {
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
}
}
/*
* @return r the sum of two points of G1
*/
function plus(
G1Point memory p1,
G1Point memory p2
) internal view returns (G1Point memory r) {
uint256[4] memory input = [
p1.X, p1.Y,
p2.X, p2.Y
];
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-add-failed");
}
/*
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalarMul(1) and p.plus(p) == p.scalarMul(2) for all
* points p.
*/
function scalarMul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input = [p.X, p.Y, s];
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-mul-failed");
}
/* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2,
G1Point memory d1,
G2Point memory d2
) internal view returns (bool) {
uint256[24] memory input = [
a1.X, a1.Y, a2.X[0], a2.X[1], a2.Y[0], a2.Y[1],
b1.X, b1.Y, b2.X[0], b2.X[1], b2.Y[0], b2.Y[1],
c1.X, c1.Y, c2.X[0], c2.X[1], c2.Y[0], c2.Y[1],
d1.X, d1.Y, d2.X[0], d2.X[1], d2.Y[0], d2.Y[1]
];
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, mul(24, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
}
contract Verifier {
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[<%vk_ic_length%>] IC;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(<%vk_alfa1%>);
vk.beta2 = Pairing.G2Point(<%vk_beta2%>);
vk.gamma2 = Pairing.G2Point(<%vk_gamma2%>);
vk.delta2 = Pairing.G2Point(<%vk_delta2%>);
<%vk_ic_pts%>
}
/*
* @returns Whether the proof is valid given the hardcoded verifying key
* above and the public inputs
*/
function verifyProof(
bytes memory proof,
uint256[<%vk_input_length%>] memory input
) public view returns (bool) {
uint256[8] memory p = abi.decode(proof, (uint256[8]));
for (uint8 i = 0; i < p.length; i++) {
// Make sure that each element in the proof is less than the prime q
require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q");
}
Pairing.G1Point memory proofA = Pairing.G1Point(p[0], p[1]);
Pairing.G2Point memory proofB = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]);
Pairing.G1Point memory proofC = Pairing.G1Point(p[6], p[7]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vkX
Pairing.G1Point memory vkX = vk.IC[0];
for (uint256 i = 0; i < input.length; i++) {
// Make sure that every input is less than the snark scalar field
require(input[i] < SNARK_SCALAR_FIELD, "verifier-input-gte-snark-scalar-field");
vkX = Pairing.plus(vkX, Pairing.scalarMul(vk.IC[i + 1], input[i]));
}
return Pairing.pairing(
Pairing.negate(proofA),
proofB,
vk.alfa1,
vk.beta2,
vkX,
vk.gamma2,
proofC,
vk.delta2
);
}
}

94
src/wtns_reader.rs Normal file
View File

@ -0,0 +1,94 @@
use byteorder::{ReadBytesExt, LittleEndian};
use std::io::{Read, Result, ErrorKind, Error};
use bellman_ce::pairing::{
Engine,
ff::{
Field, PrimeField, PrimeFieldRepr,
}
};
pub struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub witness_len: u32,
}
pub struct WTNSFile<E: Engine> {
pub version: u32,
pub header: Header,
pub witness: Vec<E::Fr>,
}
fn read_field<R: Read, E: Engine>(mut reader: R) -> Result<E::Fr> {
let mut repr = E::Fr::zero().into_repr();
repr.read_le(&mut reader)?;
let fr = E::Fr::from_repr(repr)
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
Ok(fr)
}
fn read_header<R: Read>(mut reader: R, size: u64) -> Result<Header> {
let field_size = reader.read_u32::<LittleEndian>()?;
let mut prime_size = vec![0u8; field_size as usize];
reader.read_exact(&mut prime_size)?;
//if size != 32 + field_size as u64 {
if size != 4 + 32 + 4 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid header section size"))
}
Ok(Header {
field_size,
prime_size,
witness_len: reader.read_u32::<LittleEndian>()?,
})
}
fn read_witness<R: Read, E:Engine>(mut reader: R, size: u64, header: &Header) -> Result<Vec<E::Fr>> {
if size != (header.witness_len * header.field_size) as u64 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid witness section size"));
}
let mut result = Vec::with_capacity(header.witness_len as usize);
for _ in 0..header.witness_len {
result.push(read_field::<&mut R, E>(&mut reader)?);
}
Ok(result)
}
pub fn read<E: Engine, R: Read>(mut reader: R) -> Result<WTNSFile<E>> {
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
if magic != [119, 116, 110, 115] { // magic = "wtns"
return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number"))
}
let version = reader.read_u32::<LittleEndian>()?;
if version > 2 {
return Err(Error::new(ErrorKind::InvalidData, "Unsupported version"))
}
let _num_sections = reader.read_u32::<LittleEndian>()?;
// todo: rewrite this to support different section order and unknown sections
// todo: handle sec_size correctly
let sec_type = reader.read_u32::<LittleEndian>()?;
if sec_type != 1 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid section type"));
}
let sec_size = reader.read_u64::<LittleEndian>()?;
let header = read_header(&mut reader, sec_size)?;
if header.field_size != 32 {
return Err(Error::new(ErrorKind::InvalidData, "This parser only supports 32-byte fields"))
}
if header.prime_size != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") {
return Err(Error::new(ErrorKind::InvalidData, "This parser only supports bn256"))
}
let sec_type = reader.read_u32::<LittleEndian>()?;
if sec_type != 2 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid section type"));
}
let sec_size = reader.read_u64::<LittleEndian>()?;
let witness = read_witness::<&mut R, E>(&mut reader, sec_size, &header)?;
Ok(WTNSFile { version, header, witness })
}

22
test.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/sh
set -e
# Compile the circuit
npx circom -rw
# Do a local trusted setup, generate params.bin
cargo run --release setup
# Export proving and verifying keys compatible with snarkjs and websnark
cargo run --release export-keys
# generate solidity verifier
cargo run --release generate-verifier
# generate and verify proof
npx snarkjs calculatewitness # witness is still generated by snarkjs
cargo run --release prove
cargo run --release verify
# Double check by verifying the same proof with snarkjs
npx snarkjs verify