diff --git a/groth16_verifier/Cargo.lock b/groth16_verifier/Cargo.lock index 06c188e0..94d1bae8 100644 --- a/groth16_verifier/Cargo.lock +++ b/groth16_verifier/Cargo.lock @@ -14,6 +14,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -44,10 +50,32 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-std 0.5.0", ] [[package]] @@ -56,13 +84,34 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", - "itertools", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.2", + "itertools 0.13.0", + "num-bigint", + "num-integer", "num-traits", "zeroize", ] @@ -73,13 +122,13 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "digest", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits", "paste", @@ -87,6 +136,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.4.2" @@ -97,6 +166,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.106", +] + [[package]] name = "ark-ff-macros" version = "0.4.2" @@ -110,27 +189,68 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "ark-poly" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", ] +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.2", +] + [[package]] name = "ark-serialize" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ - "ark-serialize-derive", - "ark-std", + "ark-serialize-derive 0.4.2", + "ark-std 0.4.0", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive 0.5.0", + "ark-std 0.5.0", + "arrayvec", "digest", "num-bigint", ] @@ -146,6 +266,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "ark-std" version = "0.4.0" @@ -156,6 +287,22 @@ dependencies = [ "rand", ] +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.4.0" @@ -308,14 +455,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" dependencies = [ - "quote", - "syn 2.0.106", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -444,6 +597,21 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -488,6 +656,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "either" version = "1.13.0" @@ -512,6 +692,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -587,6 +787,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -607,6 +816,19 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] [[package]] name = "heck" @@ -704,6 +926,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -1146,11 +1377,11 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "soroban-builtin-sdk-macros" -version = "23.0.1" +version = "25.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9336adeabcd6f636a4e0889c8baf494658ef5a3c4e7e227569acd2ce9091e85" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" dependencies = [ - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.106", @@ -1158,9 +1389,9 @@ dependencies = [ [[package]] name = "soroban-env-common" -version = "23.0.1" +version = "25.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00067f52e8bbf1abf0de03fe3e2fbb06910893cfbe9a7d9093d6425658833ff3" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" dependencies = [ "arbitrary", "crate-git-revision", @@ -1177,9 +1408,9 @@ dependencies = [ [[package]] name = "soroban-env-guest" -version = "23.0.1" +version = "25.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd1e40963517b10963a8e404348d3fe6caf9c278ac47a6effd48771297374d6" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" dependencies = [ "soroban-env-common", "static_assertions", @@ -1187,14 +1418,15 @@ dependencies = [ [[package]] name = "soroban-env-host" -version = "23.0.1" +version = "25.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9766c5ad78e9d8ae10afbc076301f7d610c16407a1ebb230766dbe007a48725" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" dependencies = [ "ark-bls12-381", - "ark-ec", - "ark-ff", - "ark-serialize", + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", "curve25519-dalek", "ecdsa", "ed25519-dalek", @@ -1217,17 +1449,17 @@ dependencies = [ "soroban-env-common", "soroban-wasmi", "static_assertions", - "stellar-strkey", + "stellar-strkey 0.0.13", "wasmparser", ] [[package]] name = "soroban-env-macros" -version = "23.0.1" +version = "25.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e6a1c5844257ce96f5f54ef976035d5bd0ee6edefaf9f5e0bcb8ea4b34228c" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" dependencies = [ - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "serde", @@ -1237,21 +1469,32 @@ dependencies = [ ] [[package]] -name = "soroban-groth16-verifier-contract" +name = "soroban-groth16-verifier-bls12-381" version = "0.0.0" dependencies = [ "ark-bls12-381", - "ark-ec", - "ark-ff", - "ark-serialize", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "soroban-sdk", +] + +[[package]] +name = "soroban-groth16-verifier-bn254" +version = "0.0.0" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "soroban-sdk", ] [[package]] name = "soroban-ledger-snapshot" -version = "23.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845f16b4f451466641cb90276e661eba4061c4836179ca17fc3eb1b50e266af0" +checksum = "ab9d1bfa6f7d57307bf8241789b13d3703438e7afa0527aa098a357ef757d3a2" dependencies = [ "serde", "serde_json", @@ -1263,9 +1506,9 @@ dependencies = [ [[package]] name = "soroban-sdk" -version = "23.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c11b7f617a5b355ad8eba4c6ace360d735acc45517447ea832ebe176ca90d0" +checksum = "9953e782d6da30974eea520c2b5f624c28bbc518c3bb926ec581242dd3f9d2a2" dependencies = [ "arbitrary", "bytes-lit", @@ -1281,18 +1524,19 @@ dependencies = [ "soroban-env-host", "soroban-ledger-snapshot", "soroban-sdk-macros", - "stellar-strkey", + "stellar-strkey 0.0.16", + "visibility", ] [[package]] name = "soroban-sdk-macros" -version = "23.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4d963b7368dd518e9190ee4785df27c260b9bb765069e6d90898f33f83166d" +checksum = "8a8cecb6acc735670dad3303c6a9d2b47e51adfb11224ad5a8ced55fd7b0a600" dependencies = [ "darling", "heck", - "itertools", + "itertools 0.10.5", "macro-string", "proc-macro2", "quote", @@ -1306,9 +1550,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "23.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "156a0803ed3f91f7ef159152421aec4441d33cf62e78ae4935f690a2c7798fa0" +checksum = "c79501d0636f86fe2c9b1dd7e88b9397415b3493a59b34f466abd7758c84b92b" dependencies = [ "base64", "stellar-xdr", @@ -1318,9 +1562,9 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "23.0.1" +version = "25.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a49542f65185e4f0040354a1143289d3725df81397114053ce8602fb8c5bee" +checksum = "b520b5fb013fde70796d9a6057591f53817aa0c38f8bad460126f97f59394af9" dependencies = [ "prettyplease", "proc-macro2", @@ -1361,6 +1605,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1377,11 +1627,22 @@ dependencies = [ "data-encoding", ] +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + [[package]] name = "stellar-xdr" -version = "23.0.0" +version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d2848e1694b0c8db81fd812bfab5ea71ee28073e09ccc45620ef3cf7a75a9b" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" dependencies = [ "arbitrary", "base64", @@ -1393,7 +1654,7 @@ dependencies = [ "serde", "serde_with", "sha2", - "stellar-strkey", + "stellar-strkey 0.0.13", ] [[package]] @@ -1499,6 +1760,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/groth16_verifier/Cargo.toml b/groth16_verifier/Cargo.toml index 25169411..7b7de84d 100644 --- a/groth16_verifier/Cargo.toml +++ b/groth16_verifier/Cargo.toml @@ -1,23 +1,15 @@ -[package] -name = "soroban-groth16-verifier-contract" -version = "0.0.0" -edition = "2021" -publish = false -rust-version = "1.89.0" +[workspace] +members = ["bls12-381", "bn254"] +resolver = "2" -[lib] -crate-type = ["cdylib"] -doctest = false +[workspace.package] +rust-version = "1.89.0" -[dependencies] -soroban-sdk = { version = "23.0.1" } +[workspace.dependencies] +soroban-sdk = { version = "25.0.2" } -[dev-dependencies] -soroban-sdk = { version = "23.0.1", features = ["testutils"] } -ark-bls12-381 = { version = "0.4.0"} -ark-serialize = { version = "0.4.2"} -ark-ff = { version = "0.4.2"} -ark-ec = { version = "0.4.2"} +[workspace.dev-dependencies] +soroban-sdk = { version = "25.0.2", features = ["testutils"] } [profile.release] opt-level = "z" diff --git a/groth16_verifier/Makefile b/groth16_verifier/Makefile index b9719346..1f1d801a 100644 --- a/groth16_verifier/Makefile +++ b/groth16_verifier/Makefile @@ -3,12 +3,19 @@ default: build all: test test: build - cargo test + cargo test --workspace build: - stellar contract build - @ls -l target/wasm32v1-none/release/*.wasm - + stellar contract build --package soroban-groth16-verifier-bls12-381 + stellar contract build --package soroban-groth16-verifier-bn254 + mkdir -p opt/ + stellar contract optimize \ + --wasm target/wasm32v1-none/release/soroban_groth16_verifier_bls12_381.wasm \ + --wasm-out opt/soroban_groth16_verifier_bls12_381.wasm + stellar contract optimize \ + --wasm target/wasm32v1-none/release/soroban_groth16_verifier_bn254.wasm \ + --wasm-out opt/soroban_groth16_verifier_bn254.wasm + @ls -l opt/*.wasm fmt: cargo fmt --all diff --git a/groth16_verifier/README b/groth16_verifier/README index bef4649f..528df29d 100644 --- a/groth16_verifier/README +++ b/groth16_verifier/README @@ -1,23 +1,11 @@ -# Groth16 Verifier Contract +# Groth16 Verifier Examples -A demonstration of a Groth16 zero-knowledge proof verifier implemented as a Soroban smart contract. +This directory contains two Groth16 verifier contracts: -The proof and verification key are generated following the Circom2 (circom compiler 2.2.1) [getting-started guide](https://docs.circom.io/getting-started/installation/). +- BLS12-381 implementation: [bls12-381](bls12-381) +- BN254 implementation: [bn254](bn254) -The computation demonstrates a simple multiplication circuit: `a * b = c`, where: -- `a` and `b` are private inputs -- `c` is the public output - -The `./data` directory contains all input files (circuit definition, inputs) and generated outputs. For proof verification, three key files are required: -- [proof.json](./data/proof.json) - Contains the zero-knowledge proof -- [verification_key.json](./data/verification_key.json) - Contains the verification key -- [public.json](./data/public.json) - Contains the public inputs/outputs - -Other intermediate artifacts, including witness generation code and outputs from the "Powers of Tau" ceremony, are included in `./data/auxiliary` for reproducibility. - -The [contract implementation](./src/lib.rs) is translated from the auto-generated [Solidity contract](./data/multiplier2_js/verifier.sol). The [test suite](./src/test.rs) demonstrates off-chain parsing of the proof and verification key, along with successful contract execution. - -This example was presented at the [Stellar Developer Meeting - 12/19/2024](https://www.youtube.com/watch?v=51SitOUZySk&list=PLmr3tp_7-7Gg5IAsJ0VlgfMoh-aTmbQmh&index=4) to demonstrate the BLS12-381 features. +Each subpackage includes its own contract, tests, and data artifacts. Shared Rust toolchain configuration remains at the workspace root. ## ⚠️ WARNING: Demonstration Use Only diff --git a/groth16_verifier/bls12-381/Cargo.toml b/groth16_verifier/bls12-381/Cargo.toml new file mode 100644 index 00000000..adf2eeae --- /dev/null +++ b/groth16_verifier/bls12-381/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "soroban-groth16-verifier-bls12-381" +version = "0.0.0" +edition = "2021" +publish = false +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } +ark-bls12-381 = { version = "0.4.0" } +ark-serialize = { version = "0.4.2" } +ark-ff = { version = "0.4.2" } +ark-ec = { version = "0.4.2" } + diff --git a/groth16_verifier/bls12-381/Makefile b/groth16_verifier/bls12-381/Makefile new file mode 100644 index 00000000..ad9d3374 --- /dev/null +++ b/groth16_verifier/bls12-381/Makefile @@ -0,0 +1,16 @@ +default: build + +all: test + +test: build + cargo test + +build: + stellar contract build + @ls -l ../target/wasm32v1-none/release/*.wasm + +fmt: + cargo fmt --all + +clean: + cargo clean diff --git a/groth16_verifier/bls12-381/README.md b/groth16_verifier/bls12-381/README.md new file mode 100644 index 00000000..7c55395e --- /dev/null +++ b/groth16_verifier/bls12-381/README.md @@ -0,0 +1,28 @@ +# Groth16 Verifier Contract (BLS12-381) + +A demonstration of a Groth16 zero-knowledge proof verifier implemented as a Soroban smart contract. + +The proof and verification key are generated following the Circom2 (circom compiler 2.2.1) [getting-started guide](https://docs.circom.io/getting-started/installation/). + +The computation demonstrates a simple multiplication circuit: `a * b = c`, where: +- `a` and `b` are private inputs +- `c` is the public output + +The `./data` directory contains all input files (circuit definition, inputs) and generated outputs. For proof verification, three key files are required: +- [proof.json](data/proof.json) - Contains the zero-knowledge proof +- [verification_key.json](data/verification_key.json) - Contains the verification key +- [public.json](data/public.json) - Contains the public inputs/outputs + +Other intermediate artifacts, including witness generation code and outputs from the "Powers of Tau" ceremony, are included in [data/auxiliary](data/auxiliary) for reproducibility. + +The contract implementation in [src/lib.rs](src/lib.rs) is translated from the auto-generated Solidity contract. The test suite in [src/test.rs](src/test.rs) demonstrates off-chain parsing of the proof and verification key, along with successful contract execution. + +This example was presented at the [Stellar Developer Meeting - 12/19/2024](https://www.youtube.com/watch?v=51SitOUZySk&list=PLmr3tp_7-7Gg5IAsJ0VlgfMoh-aTmbQmh&index=4) to demonstrate the BLS12-381 features. + +## ⚠️ WARNING: Demonstration Use Only + +**This project is for demonstration purposes only.** +- It has **not** undergone security auditing +- It is **not** safe for use in production environments + +**Use at your own risk.** diff --git a/groth16_verifier/data/auxiliary/input.json b/groth16_verifier/bls12-381/data/auxiliary/input.json similarity index 100% rename from groth16_verifier/data/auxiliary/input.json rename to groth16_verifier/bls12-381/data/auxiliary/input.json diff --git a/groth16_verifier/data/auxiliary/multiplier2.circom b/groth16_verifier/bls12-381/data/auxiliary/multiplier2.circom similarity index 100% rename from groth16_verifier/data/auxiliary/multiplier2.circom rename to groth16_verifier/bls12-381/data/auxiliary/multiplier2.circom diff --git a/groth16_verifier/data/auxiliary/multiplier2_0000.zkey b/groth16_verifier/bls12-381/data/auxiliary/multiplier2_0000.zkey similarity index 100% rename from groth16_verifier/data/auxiliary/multiplier2_0000.zkey rename to groth16_verifier/bls12-381/data/auxiliary/multiplier2_0000.zkey diff --git a/groth16_verifier/data/auxiliary/multiplier2_0001.zkey b/groth16_verifier/bls12-381/data/auxiliary/multiplier2_0001.zkey similarity index 100% rename from groth16_verifier/data/auxiliary/multiplier2_0001.zkey rename to groth16_verifier/bls12-381/data/auxiliary/multiplier2_0001.zkey diff --git a/groth16_verifier/data/auxiliary/pot12_0000.ptau b/groth16_verifier/bls12-381/data/auxiliary/pot12_0000.ptau similarity index 100% rename from groth16_verifier/data/auxiliary/pot12_0000.ptau rename to groth16_verifier/bls12-381/data/auxiliary/pot12_0000.ptau diff --git a/groth16_verifier/data/auxiliary/pot12_0001.ptau b/groth16_verifier/bls12-381/data/auxiliary/pot12_0001.ptau similarity index 100% rename from groth16_verifier/data/auxiliary/pot12_0001.ptau rename to groth16_verifier/bls12-381/data/auxiliary/pot12_0001.ptau diff --git a/groth16_verifier/data/auxiliary/pot12_final.ptau b/groth16_verifier/bls12-381/data/auxiliary/pot12_final.ptau similarity index 100% rename from groth16_verifier/data/auxiliary/pot12_final.ptau rename to groth16_verifier/bls12-381/data/auxiliary/pot12_final.ptau diff --git a/groth16_verifier/data/auxiliary/witness.wtns b/groth16_verifier/bls12-381/data/auxiliary/witness.wtns similarity index 100% rename from groth16_verifier/data/auxiliary/witness.wtns rename to groth16_verifier/bls12-381/data/auxiliary/witness.wtns diff --git a/groth16_verifier/data/proof.json b/groth16_verifier/bls12-381/data/proof.json similarity index 100% rename from groth16_verifier/data/proof.json rename to groth16_verifier/bls12-381/data/proof.json diff --git a/groth16_verifier/data/public.json b/groth16_verifier/bls12-381/data/public.json similarity index 100% rename from groth16_verifier/data/public.json rename to groth16_verifier/bls12-381/data/public.json diff --git a/groth16_verifier/data/verification_key.json b/groth16_verifier/bls12-381/data/verification_key.json similarity index 100% rename from groth16_verifier/data/verification_key.json rename to groth16_verifier/bls12-381/data/verification_key.json diff --git a/groth16_verifier/src/lib.rs b/groth16_verifier/bls12-381/src/lib.rs similarity index 100% rename from groth16_verifier/src/lib.rs rename to groth16_verifier/bls12-381/src/lib.rs diff --git a/groth16_verifier/bls12-381/src/test.rs b/groth16_verifier/bls12-381/src/test.rs new file mode 100644 index 00000000..824b417a --- /dev/null +++ b/groth16_verifier/bls12-381/src/test.rs @@ -0,0 +1,196 @@ +#![cfg(test)] +extern crate std; + +use ark_bls12_381::{Fq, Fq2}; +use ark_serialize::CanonicalSerialize; +use core::str::FromStr; +use soroban_sdk::{ + crypto::bls12_381::{Fr, G1Affine, G2Affine, G1_SERIALIZED_SIZE, G2_SERIALIZED_SIZE}, + Env, Vec, U256, +}; + +use crate::{Groth16Verifier, Groth16VerifierClient, Proof, VerificationKey}; + +const ALPHA_X: &str = "851850525556173310373115880154698084608631105506432893865500290442025919078535925294035153152030470398262539759609"; +const ALPHA_Y: &str = "2637289349983507610125993281171282870664683328789064436670091381805667870657250691837988574635646688089951719927247"; + +const BETA_X1: &str = "1312620381151154625549413690218290437739613987001512553647554932245743783919690104921577716179019375920325686841943"; +const BETA_X2: &str = "1853421227732662200477195678252233549930451033531229987959164216695698667330234953033341200627605777603511819497457"; +const BETA_Y1: &str = "3215807833988244618006117550809420301978856703407297742347804415291049013404133666905173282837707341742014140541018"; +const BETA_Y2: &str = "812366606879346135498483310623227330050424196838294715759414425317592599094348477520229174120664109186562798527696"; + +const GAMMA_X1: &str = "352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160"; +const GAMMA_X2: &str = "3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758"; +const GAMMA_Y1: &str = "1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905"; +const GAMMA_Y2: &str = "927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582"; + +const DELTA_X1: &str = "2981843938988033214458466658185878126396080429969635248100956025957789319926032198626745120548947333202362392267114"; +const DELTA_X2: &str = "2236695112259305382987038341098587500598216646308901956168137697892380899086228863246537938263638056666003066263342"; +const DELTA_Y1: &str = "717163810166643254871951856655865822196000925757284470845197358532703820821048809982340614428800986999944933231635"; +const DELTA_Y2: &str = "3496058064578305387608803828034117220735807855182872031001942587835768203820179263722136810383631418598310938506798"; + +const IC0_X: &str = "829685638389803071404995253486571779300247099942205634643821309129201420207693030476756893332812706176564514055395"; +const IC0_Y: &str = "3455508165409829148751617737772894557887792278044850553785496869183933597103951941805834639972489587640583544390358"; +const IC1_X: &str = "2645559270376031734407122278942646687260452979296081924477586893972449945444985371392950465676350735694002713633589"; +const IC1_Y: &str = "2241039659097418315097403108596818813895651201896886552939297756980670248638746432560267634304593609165964274111037"; + +const PI_A_X: &str = "314442236668110257304682488877371582255161413673331360366570443799415414639292047869143313601702131653514009114222"; +const PI_A_Y: &str = "2384632327855835824635705027009217874826122107057894594162233214798350178691568018290025994699762298534539543934607"; +const PI_B_X1: &str = "428844167033934720609657613212495751617651348480870890908850335525890280786532876634895457032623422366474694342656"; +const PI_B_X2: &str = "3083139526360252775789959298805261067575555607578161553873977966165446991459924053189383038704105379290158793353905"; +const PI_B_Y1: &str = "1590919422794657666432683000821892403620510405626533455397042191265963587891653562867091397248216891852168698286910"; +const PI_B_Y2: &str = "3617931039814164588401589536353142503544155307022467123698224064329647390280346725086550997337076315487486714327146"; +const PI_C_X: &str = "3052934797502613468327963344215392478880720823583493172692775426011388142569325036386650708808320216973179639719187"; +const PI_C_Y: &str = "2028185281516938724429867827057869371578022471499780916652824405212207527699373814371051328341613972789943854539597"; + +fn g1_from_coords(env: &Env, x: &str, y: &str) -> G1Affine { + let ark_g1 = ark_bls12_381::G1Affine::new(Fq::from_str(x).unwrap(), Fq::from_str(y).unwrap()); + let mut buf = [0u8; G1_SERIALIZED_SIZE]; + ark_g1.serialize_uncompressed(&mut buf[..]).unwrap(); + G1Affine::from_array(env, &buf) +} + +fn g2_from_coords(env: &Env, x1: &str, x2: &str, y1: &str, y2: &str) -> G2Affine { + let x = Fq2::new(Fq::from_str(x1).unwrap(), Fq::from_str(x2).unwrap()); + let y = Fq2::new(Fq::from_str(y1).unwrap(), Fq::from_str(y2).unwrap()); + let ark_g2 = ark_bls12_381::G2Affine::new(x, y); + let mut buf = [0u8; G2_SERIALIZED_SIZE]; + ark_g2.serialize_uncompressed(&mut buf[..]).unwrap(); + G2Affine::from_array(env, &buf) +} + +fn create_client(e: &Env) -> Groth16VerifierClient<'_> { + Groth16VerifierClient::new(e, &e.register(Groth16Verifier {}, ())) +} + +mod groth16_verifier_contract { + soroban_sdk::contractimport!(file = "../opt/soroban_groth16_verifier_bls12_381.wasm"); +} + +fn build_vk(env: &Env) -> VerificationKey { + VerificationKey { + alpha: g1_from_coords(env, ALPHA_X, ALPHA_Y), + beta: g2_from_coords(env, BETA_X1, BETA_X2, BETA_Y1, BETA_Y2), + gamma: g2_from_coords(env, GAMMA_X1, GAMMA_X2, GAMMA_Y1, GAMMA_Y2), + delta: g2_from_coords(env, DELTA_X1, DELTA_X2, DELTA_Y1, DELTA_Y2), + ic: Vec::from_array( + env, + [ + g1_from_coords(env, IC0_X, IC0_Y), + g1_from_coords(env, IC1_X, IC1_Y), + ], + ), + } +} + +fn build_proof(env: &Env) -> Proof { + Proof { + a: g1_from_coords(env, PI_A_X, PI_A_Y), + b: g2_from_coords(env, PI_B_X1, PI_B_X2, PI_B_Y1, PI_B_Y2), + c: g1_from_coords(env, PI_C_X, PI_C_Y), + } +} + +fn public_output(env: &Env, value: u32) -> Vec { + Vec::from_array(env, [Fr::from_u256(U256::from_u32(env, value))]) +} + +fn build_vk_wasm(env: &Env) -> groth16_verifier_contract::VerificationKey { + groth16_verifier_contract::VerificationKey { + alpha: g1_from_coords(env, ALPHA_X, ALPHA_Y).into(), + beta: g2_from_coords(env, BETA_X1, BETA_X2, BETA_Y1, BETA_Y2).into(), + gamma: g2_from_coords(env, GAMMA_X1, GAMMA_X2, GAMMA_Y1, GAMMA_Y2).into(), + delta: g2_from_coords(env, DELTA_X1, DELTA_X2, DELTA_Y1, DELTA_Y2).into(), + ic: Vec::from_array( + env, + [ + g1_from_coords(env, IC0_X, IC0_Y).into(), + g1_from_coords(env, IC1_X, IC1_Y).into(), + ], + ), + } +} + +fn build_proof_wasm(env: &Env) -> groth16_verifier_contract::Proof { + groth16_verifier_contract::Proof { + a: g1_from_coords(env, PI_A_X, PI_A_Y).into(), + b: g2_from_coords(env, PI_B_X1, PI_B_X2, PI_B_Y1, PI_B_Y2).into(), + c: g1_from_coords(env, PI_C_X, PI_C_Y).into(), + } +} + +fn public_output_wasm(env: &Env, value: u32) -> Vec { + Vec::from_array(env, [U256::from_u32(env, value)]) +} + +#[test] +fn test() { + // Initialize the test environment + let env = Env::default(); + + // Load verification key components (copied from `data/verification_key.json`) + // These values are pre-computed for the circuit that verifies a*b = c + // where a=3, b=11, c=33 and only c is public. + let vk = build_vk(&env); + let proof = build_proof(&env); + + // Create the contract client + let client = create_client(&env); + + // Test Case 1: Verify the proof with the correct public output (33, copied from `data/public.json`) + let output = public_output(&env, 33); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, true); + + // Print out the budget report showing CPU and memory cost breakdown for + // different operations (zero-value operations omitted for brevity) + env.cost_estimate().budget().print(); + /* + ================================================================= + Cpu limit: 100000000; used: 40968821 + Mem limit: 41943040; used: 297494 + ================================================================= + CostType cpu_insns mem_bytes + MemAlloc 12089 3401 + MemCpy 3091 0 + MemCmp 928 0 + VisitObject 5917 0 + ComputeSha256Hash 3738 0 + Bls12381EncodeFp 2644 0 + Bls12381DecodeFp 29550 0 + Bls12381G1CheckPointOnCurve 13538 0 + Bls12381G1CheckPointInSubgroup 3652550 0 + Bls12381G2CheckPointOnCurve 23684 0 + Bls12381G2CheckPointInSubgroup 4231288 0 + Bls12381G1ProjectiveToAffine 185284 0 + Bls12381G1Add 7689 0 + Bls12381G1Mul 2458985 0 + Bls12381Pairing 30335852 294093 + Bls12381FrFromU256 1994 0 + // ... zero-value rows omitted ... + ================================================================= + */ + + // Test Case 2: Verify the proof with an incorrect public output (22) + let output = public_output(&env, 22); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, false); +} + +#[test] +fn test_running_contract_as_wasm() { + let env = Env::default(); + env.cost_estimate().budget().reset_unlimited(); + + let contract_id = env.register(groth16_verifier_contract::WASM, ()); + let client = groth16_verifier_contract::Client::new(&env, &contract_id); + + let vk = build_vk_wasm(&env); + let proof = build_proof_wasm(&env); + + let output = public_output_wasm(&env, 33); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, true); + + env.cost_estimate().budget().print(); +} diff --git a/groth16_verifier/bn254/Cargo.toml b/groth16_verifier/bn254/Cargo.toml new file mode 100644 index 00000000..e0c1d0f6 --- /dev/null +++ b/groth16_verifier/bn254/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "soroban-groth16-verifier-bn254" +version = "0.0.0" +edition = "2021" +publish = false +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } +ark-bn254 = { version = "0.5.0" } +ark-serialize = { version = "0.5.0" } +ark-ff = { version = "0.5.0" } +ark-ec = { version = "0.5.0" } + diff --git a/groth16_verifier/bn254/Makefile b/groth16_verifier/bn254/Makefile new file mode 100644 index 00000000..ad9d3374 --- /dev/null +++ b/groth16_verifier/bn254/Makefile @@ -0,0 +1,16 @@ +default: build + +all: test + +test: build + cargo test + +build: + stellar contract build + @ls -l ../target/wasm32v1-none/release/*.wasm + +fmt: + cargo fmt --all + +clean: + cargo clean diff --git a/groth16_verifier/bn254/README.md b/groth16_verifier/bn254/README.md new file mode 100644 index 00000000..1ad78818 --- /dev/null +++ b/groth16_verifier/bn254/README.md @@ -0,0 +1,24 @@ +# Groth16 Verifier Contract (BN254) + +A Groth16 verifier for the BN254 curve implemented using Soroban SDK crypto types. + +The computation demonstrates a simple multiplication circuit: `a * b = c`, where: +- `a` and `b` are private inputs +- `c` is the public output + +The `./data` directory contains all input files (circuit definition, inputs) and generated outputs. For proof verification, three key files are required: +- [proof.json](data/proof.json) - Contains the zero-knowledge proof +- [verification_key.json](data/verification_key.json) - Contains the verification key +- [public.json](data/public.json) - Contains the public inputs/outputs + +Other intermediate artifacts, including witness generation code and outputs from the "Powers of Tau" ceremony, are included in [data/auxiliary](data/auxiliary) for reproducibility. + +The contract implementation in [src/lib.rs](src/lib.rs) is translated from the auto-generated Solidity contract produced by snarkjs. The test suite in [src/test.rs](src/test.rs) demonstrates off-chain parsing of the proof and verification key, along with successful contract execution. + +## ⚠️ WARNING: Demonstration Use Only + +**This project is for demonstration purposes only.** +- It has **not** undergone security auditing +- It is **not** safe for use in production environments + +**Use at your own risk.** diff --git a/groth16_verifier/bn254/data/auxiliary/input.json b/groth16_verifier/bn254/data/auxiliary/input.json new file mode 100644 index 00000000..c7e0fc65 --- /dev/null +++ b/groth16_verifier/bn254/data/auxiliary/input.json @@ -0,0 +1 @@ +{"a": 3, "b": 11} diff --git a/groth16_verifier/bn254/data/auxiliary/multiplier2.circom b/groth16_verifier/bn254/data/auxiliary/multiplier2.circom new file mode 100644 index 00000000..080377d0 --- /dev/null +++ b/groth16_verifier/bn254/data/auxiliary/multiplier2.circom @@ -0,0 +1,16 @@ +pragma circom 2.0.0; + +/*This circuit template checks that c is the multiplication of a and b.*/ + +template Multiplier2 () { + + // Declaration of signals. + signal input a; + signal input b; + signal output c; + + // Constraints. + c <== a * b; +} + +component main = Multiplier2(); diff --git a/groth16_verifier/bn254/data/auxiliary/multiplier2_0000.zkey b/groth16_verifier/bn254/data/auxiliary/multiplier2_0000.zkey new file mode 100644 index 00000000..b8d5094e Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/multiplier2_0000.zkey differ diff --git a/groth16_verifier/bn254/data/auxiliary/multiplier2_0001.zkey b/groth16_verifier/bn254/data/auxiliary/multiplier2_0001.zkey new file mode 100644 index 00000000..a475b316 Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/multiplier2_0001.zkey differ diff --git a/groth16_verifier/bn254/data/auxiliary/pot12_0000.ptau b/groth16_verifier/bn254/data/auxiliary/pot12_0000.ptau new file mode 100644 index 00000000..d1fc4fa8 Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/pot12_0000.ptau differ diff --git a/groth16_verifier/bn254/data/auxiliary/pot12_0001.ptau b/groth16_verifier/bn254/data/auxiliary/pot12_0001.ptau new file mode 100644 index 00000000..59526adc Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/pot12_0001.ptau differ diff --git a/groth16_verifier/bn254/data/auxiliary/pot12_final.ptau b/groth16_verifier/bn254/data/auxiliary/pot12_final.ptau new file mode 100644 index 00000000..b6d175c1 Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/pot12_final.ptau differ diff --git a/groth16_verifier/bn254/data/auxiliary/witness.wtns b/groth16_verifier/bn254/data/auxiliary/witness.wtns new file mode 100644 index 00000000..36e6f6d8 Binary files /dev/null and b/groth16_verifier/bn254/data/auxiliary/witness.wtns differ diff --git a/groth16_verifier/bn254/data/proof.json b/groth16_verifier/bn254/data/proof.json new file mode 100644 index 00000000..e6ffba10 --- /dev/null +++ b/groth16_verifier/bn254/data/proof.json @@ -0,0 +1,28 @@ +{ + "pi_a": [ + "15056319861143982370511851795100655862674952298862168185142634150526446898932", + "20524016742028017914278639014573029696397580893761136931992899239091515061583", + "1" + ], + "pi_b": [ + [ + "1739426650003613298637521076298027612785708580102733347125679950635649770676", + "2964793715073763051183683532836939129978676824115763049942969571484972008340" + ], + [ + "231714500818561961964881885199979457754178468650240635167721171785635224632", + "16949586399773881191905677229599029076778019197530892858153210038293513748836" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "19305753993632836293634613984434718007247114328469217861805683289168510519612", + "7647827534035254388770083633685274061394204244398195918459217009560805334325", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} \ No newline at end of file diff --git a/groth16_verifier/bn254/data/public.json b/groth16_verifier/bn254/data/public.json new file mode 100644 index 00000000..bf203097 --- /dev/null +++ b/groth16_verifier/bn254/data/public.json @@ -0,0 +1,3 @@ +[ + "33" +] \ No newline at end of file diff --git a/groth16_verifier/bn254/data/verification_key.json b/groth16_verifier/bn254/data/verification_key.json new file mode 100644 index 00000000..41140306 --- /dev/null +++ b/groth16_verifier/bn254/data/verification_key.json @@ -0,0 +1,94 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 1, + "vk_alpha_1": [ + "20586411654224940726121807510656854616919838017362192913325723364246767479247", + "14406135834237420410898393224422728106845847411380780163009899508656542500674", + "1" + ], + "vk_beta_2": [ + [ + "10455785418957748805112801769878863871439287287746407726187317727278729655393", + "7994732166366626571659429762957445732500444789770753725565126434770874060445" + ], + [ + "6171554364750907305893121967664455952867276595633237025381859103793190603186", + "1068707785281839727180321966526799350627265679563357300928626073084686493834" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "9728641619599932167558405925032483286291264581149582657399819891409232109311", + "19241695161990167743108773954643056384459332112654898578100132363962975297059" + ], + [ + "14077793321404315919939614415644656852218893964079974656696758780514393147506", + "477138352543384316457862019198400223292705943523596124653136815176264865609" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "6939506855656366901521716354030437710939598133600496714375688090661678078314", + "20214666786638565806050044889253541429741675698759450023051129729523934998479" + ], + [ + "21715614352920538589803397893233362412390187301580051386011339238163899958386", + "18970712028760029743504135975079655212815692858112923261685501064527229227861" + ], + [ + "16577261066106757946060219005598386899888200826220923366505610641687664438214", + "941663819489153655504201947990359582124986529696654497039802020999575773632" + ] + ], + [ + [ + "9916215431929107267064109601917365748047218275039229651793460591674128257131", + "15932376725642577606873467972537095131697592198711649347860630460277509589524" + ], + [ + "12842158877606706750804665865071723289967083815223331920815809924615845544413", + "9199491889420268324353893528486173088369778623826174866763129988962654682498" + ], + [ + "5191140533885214139128597600734827797494160726147733936154947761968123930912", + "12386565945380029410464598214213279689054342267232785156055911298169028042019" + ] + ] + ], + "IC": [ + [ + "3455198239811392358031940101382607447413352702429558918793594053377990534277", + "5436753965720439827118819730906327482174186376678655329845964791381151291134", + "1" + ], + [ + "19346729925135133472054871844930104540224821692821486182760033487886118472382", + "445070660711676321158305681137628146014387270520187998858724591986469989204", + "1" + ] + ] +} \ No newline at end of file diff --git a/groth16_verifier/bn254/src/lib.rs b/groth16_verifier/bn254/src/lib.rs new file mode 100644 index 00000000..cb543f47 --- /dev/null +++ b/groth16_verifier/bn254/src/lib.rs @@ -0,0 +1,67 @@ +#![no_std] +use soroban_sdk::{ + contract, contracterror, contractimpl, contracttype, + crypto::bn254::{Bn254G1Affine, Bn254G2Affine, Fr}, + vec, Env, Vec, +}; + +#[contracterror] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum Groth16Error { + MalformedVerifyingKey = 0, +} + +#[derive(Clone)] +#[contracttype] +pub struct VerificationKey { + pub alpha: Bn254G1Affine, + pub beta: Bn254G2Affine, + pub gamma: Bn254G2Affine, + pub delta: Bn254G2Affine, + pub ic: Vec, +} + +#[derive(Clone)] +#[contracttype] +pub struct Proof { + pub a: Bn254G1Affine, + pub b: Bn254G2Affine, + pub c: Bn254G1Affine, +} + +#[contract] +pub struct Groth16Verifier; + +#[contractimpl] +impl Groth16Verifier { + pub fn verify_proof( + env: Env, + vk: VerificationKey, + proof: Proof, + pub_signals: Vec, + ) -> Result { + let bn = env.crypto().bn254(); + + // Prepare proof inputs: + // Compute vk_x = ic[0] + sum(pub_signals[i] * ic[i+1]) + if pub_signals.len() + 1 != vk.ic.len() { + return Err(Groth16Error::MalformedVerifyingKey); + } + let mut vk_x = vk.ic.get(0).unwrap(); + for (s, v) in pub_signals.iter().zip(vk.ic.iter().skip(1)) { + let prod = bn.g1_mul(&v, &s); + vk_x = bn.g1_add(&vk_x, &prod); + } + + // Compute the pairing: + // e(-A, B) * e(alpha, beta) * e(vk_x, gamma) * e(C, delta) == 1 + let neg_a = -proof.a; + let vp1 = vec![&env, neg_a, vk.alpha, vk_x, proof.c]; + let vp2 = vec![&env, proof.b, vk.beta, vk.gamma, vk.delta]; + + Ok(bn.pairing_check(vp1, vp2)) + } +} + +mod test; diff --git a/groth16_verifier/bn254/src/test.rs b/groth16_verifier/bn254/src/test.rs new file mode 100644 index 00000000..e7784b51 --- /dev/null +++ b/groth16_verifier/bn254/src/test.rs @@ -0,0 +1,207 @@ +#![cfg(test)] +extern crate std; + +use ark_bn254::{Fq, Fq2}; +use ark_ff::{BigInteger, PrimeField}; +use core::str::FromStr; +use soroban_sdk::{ + crypto::bn254::{ + Bn254G1Affine, Bn254G2Affine, Fr, BN254_G1_SERIALIZED_SIZE, BN254_G2_SERIALIZED_SIZE, + }, + Env, Vec, U256, +}; + +use crate::{Groth16Verifier, Groth16VerifierClient, Proof, VerificationKey}; + +const ALPHA_X: &str = + "20586411654224940726121807510656854616919838017362192913325723364246767479247"; +const ALPHA_Y: &str = + "14406135834237420410898393224422728106845847411380780163009899508656542500674"; + +const BETA_X1: &str = + "10455785418957748805112801769878863871439287287746407726187317727278729655393"; +const BETA_X2: &str = + "7994732166366626571659429762957445732500444789770753725565126434770874060445"; +const BETA_Y1: &str = + "6171554364750907305893121967664455952867276595633237025381859103793190603186"; +const BETA_Y2: &str = + "1068707785281839727180321966526799350627265679563357300928626073084686493834"; + +const GAMMA_X1: &str = + "10857046999023057135944570762232829481370756359578518086990519993285655852781"; +const GAMMA_X2: &str = + "11559732032986387107991004021392285783925812861821192530917403151452391805634"; +const GAMMA_Y1: &str = + "8495653923123431417604973247489272438418190587263600148770280649306958101930"; +const GAMMA_Y2: &str = + "4082367875863433681332203403145435568316851327593401208105741076214120093531"; + +const DELTA_X1: &str = + "9728641619599932167558405925032483286291264581149582657399819891409232109311"; +const DELTA_X2: &str = + "19241695161990167743108773954643056384459332112654898578100132363962975297059"; +const DELTA_Y1: &str = + "14077793321404315919939614415644656852218893964079974656696758780514393147506"; +const DELTA_Y2: &str = + "477138352543384316457862019198400223292705943523596124653136815176264865609"; + +const IC0_X: &str = "3455198239811392358031940101382607447413352702429558918793594053377990534277"; +const IC0_Y: &str = "5436753965720439827118819730906327482174186376678655329845964791381151291134"; +const IC1_X: &str = "19346729925135133472054871844930104540224821692821486182760033487886118472382"; +const IC1_Y: &str = "445070660711676321158305681137628146014387270520187998858724591986469989204"; + +const PI_A_X: &str = + "15056319861143982370511851795100655862674952298862168185142634150526446898932"; +const PI_A_Y: &str = + "20524016742028017914278639014573029696397580893761136931992899239091515061583"; +const PI_B_X1: &str = + "1739426650003613298637521076298027612785708580102733347125679950635649770676"; +const PI_B_X2: &str = + "2964793715073763051183683532836939129978676824115763049942969571484972008340"; +const PI_B_Y1: &str = "231714500818561961964881885199979457754178468650240635167721171785635224632"; +const PI_B_Y2: &str = + "16949586399773881191905677229599029076778019197530892858153210038293513748836"; +const PI_C_X: &str = + "19305753993632836293634613984434718007247114328469217861805683289168510519612"; +const PI_C_Y: &str = "7647827534035254388770083633685274061394204244398195918459217009560805334325"; + +fn fq_to_bytes_be(fq: &Fq) -> [u8; 32] { + let bytes = fq.into_bigint().to_bytes_be(); + let mut out = [0u8; 32]; + let start = out.len() - bytes.len(); + out[start..].copy_from_slice(&bytes); + out +} + +fn g1_from_coords(env: &Env, x: &str, y: &str) -> Bn254G1Affine { + let ark_g1 = ark_bn254::G1Affine::new(Fq::from_str(x).unwrap(), Fq::from_str(y).unwrap()); + let mut buf = [0u8; BN254_G1_SERIALIZED_SIZE]; + let x_bytes = fq_to_bytes_be(&ark_g1.x); + let y_bytes = fq_to_bytes_be(&ark_g1.y); + buf[..32].copy_from_slice(&x_bytes); + buf[32..].copy_from_slice(&y_bytes); + Bn254G1Affine::from_array(env, &buf) +} + +fn g2_from_coords(env: &Env, x1: &str, x2: &str, y1: &str, y2: &str) -> Bn254G2Affine { + let x = Fq2::new(Fq::from_str(x1).unwrap(), Fq::from_str(x2).unwrap()); + let y = Fq2::new(Fq::from_str(y1).unwrap(), Fq::from_str(y2).unwrap()); + let ark_g2 = ark_bn254::G2Affine::new(x, y); + let mut buf = [0u8; BN254_G2_SERIALIZED_SIZE]; + let x_c1 = fq_to_bytes_be(&ark_g2.x.c1); + let x_c0 = fq_to_bytes_be(&ark_g2.x.c0); + let y_c1 = fq_to_bytes_be(&ark_g2.y.c1); + let y_c0 = fq_to_bytes_be(&ark_g2.y.c0); + buf[0..32].copy_from_slice(&x_c1); + buf[32..64].copy_from_slice(&x_c0); + buf[64..96].copy_from_slice(&y_c1); + buf[96..128].copy_from_slice(&y_c0); + Bn254G2Affine::from_array(env, &buf) +} + +fn create_client(e: &Env) -> Groth16VerifierClient<'_> { + Groth16VerifierClient::new(e, &e.register(Groth16Verifier {}, ())) +} + +mod groth16_verifier_contract { + soroban_sdk::contractimport!(file = "../opt/soroban_groth16_verifier_bn254.wasm"); +} + +fn build_vk(env: &Env) -> VerificationKey { + VerificationKey { + alpha: g1_from_coords(env, ALPHA_X, ALPHA_Y), + beta: g2_from_coords(env, BETA_X1, BETA_X2, BETA_Y1, BETA_Y2), + gamma: g2_from_coords(env, GAMMA_X1, GAMMA_X2, GAMMA_Y1, GAMMA_Y2), + delta: g2_from_coords(env, DELTA_X1, DELTA_X2, DELTA_Y1, DELTA_Y2), + ic: Vec::from_array( + env, + [ + g1_from_coords(env, IC0_X, IC0_Y), + g1_from_coords(env, IC1_X, IC1_Y), + ], + ), + } +} + +fn build_proof(env: &Env) -> Proof { + Proof { + a: g1_from_coords(env, PI_A_X, PI_A_Y), + b: g2_from_coords(env, PI_B_X1, PI_B_X2, PI_B_Y1, PI_B_Y2), + c: g1_from_coords(env, PI_C_X, PI_C_Y), + } +} + +fn public_output(env: &Env, value: u32) -> Vec { + Vec::from_array(env, [Fr::from_u256(U256::from_u32(env, value))]) +} + +fn build_vk_wasm(env: &Env) -> groth16_verifier_contract::VerificationKey { + groth16_verifier_contract::VerificationKey { + alpha: g1_from_coords(env, ALPHA_X, ALPHA_Y).into(), + beta: g2_from_coords(env, BETA_X1, BETA_X2, BETA_Y1, BETA_Y2).into(), + gamma: g2_from_coords(env, GAMMA_X1, GAMMA_X2, GAMMA_Y1, GAMMA_Y2).into(), + delta: g2_from_coords(env, DELTA_X1, DELTA_X2, DELTA_Y1, DELTA_Y2).into(), + ic: Vec::from_array( + env, + [ + g1_from_coords(env, IC0_X, IC0_Y).into(), + g1_from_coords(env, IC1_X, IC1_Y).into(), + ], + ), + } +} + +fn build_proof_wasm(env: &Env) -> groth16_verifier_contract::Proof { + groth16_verifier_contract::Proof { + a: g1_from_coords(env, PI_A_X, PI_A_Y).into(), + b: g2_from_coords(env, PI_B_X1, PI_B_X2, PI_B_Y1, PI_B_Y2).into(), + c: g1_from_coords(env, PI_C_X, PI_C_Y).into(), + } +} + +fn public_output_wasm(env: &Env, value: u32) -> Vec { + Vec::from_array(env, [U256::from_u32(env, value)]) +} + +#[test] +fn test() { + // Initialize the test environment + let env = Env::default(); + + // Load verification key components (copied from `data/verification_key.json`) + // These values are pre-computed for the circuit that verifies a*b = c + // where a=3, b=11, c=33 and only c is public. + let vk = build_vk(&env); + let proof = build_proof(&env); + + // Create the contract client + let client = create_client(&env); + + // Test Case 1: Verify the proof with the correct public output (33, copied from `data/public.json`) + let output = public_output(&env, 33); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, true); + + // Test Case 2: Verify the proof with an incorrect public output (22) + let output = public_output(&env, 22); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, false); +} + +#[test] +fn test_running_contract_as_wasm() { + let env = Env::default(); + env.cost_estimate().budget().reset_unlimited(); + + let contract_id = env.register(groth16_verifier_contract::WASM, ()); + let client = groth16_verifier_contract::Client::new(&env, &contract_id); + + let vk = build_vk_wasm(&env); + let proof = build_proof_wasm(&env); + + let output = public_output_wasm(&env, 33); + let res = client.verify_proof(&vk, &proof, &output); + assert_eq!(res, true); + + env.cost_estimate().budget().print(); +} diff --git a/groth16_verifier/src/test.rs b/groth16_verifier/src/test.rs deleted file mode 100644 index 02100185..00000000 --- a/groth16_verifier/src/test.rs +++ /dev/null @@ -1,139 +0,0 @@ -#![cfg(test)] -extern crate std; - -use ark_bls12_381::{Fq, Fq2}; -use ark_serialize::CanonicalSerialize; -use core::str::FromStr; -use soroban_sdk::{ - crypto::bls12_381::{Fr, G1Affine, G2Affine, G1_SERIALIZED_SIZE, G2_SERIALIZED_SIZE}, - Env, Vec, U256, -}; - -use crate::{Groth16Verifier, Groth16VerifierClient, Proof, VerificationKey}; - -fn g1_from_coords(env: &Env, x: &str, y: &str) -> G1Affine { - let ark_g1 = ark_bls12_381::G1Affine::new(Fq::from_str(x).unwrap(), Fq::from_str(y).unwrap()); - let mut buf = [0u8; G1_SERIALIZED_SIZE]; - ark_g1.serialize_uncompressed(&mut buf[..]).unwrap(); - G1Affine::from_array(env, &buf) -} - -fn g2_from_coords(env: &Env, x1: &str, x2: &str, y1: &str, y2: &str) -> G2Affine { - let x = Fq2::new(Fq::from_str(x1).unwrap(), Fq::from_str(x2).unwrap()); - let y = Fq2::new(Fq::from_str(y1).unwrap(), Fq::from_str(y2).unwrap()); - let ark_g2 = ark_bls12_381::G2Affine::new(x, y); - let mut buf = [0u8; G2_SERIALIZED_SIZE]; - ark_g2.serialize_uncompressed(&mut buf[..]).unwrap(); - G2Affine::from_array(env, &buf) -} - -fn create_client(e: &Env) -> Groth16VerifierClient<'_> { - Groth16VerifierClient::new(e, &e.register(Groth16Verifier {}, ())) -} - -#[test] -fn test() { - // Initialize the test environment - let env = Env::default(); - - // Load verification key components (copied from `data/verification_key.json`) - // These values are pre-computed for the circuit that verifies a*b = c - // where a=3, b=11, c=33 and only c is public. - let alphax = "851850525556173310373115880154698084608631105506432893865500290442025919078535925294035153152030470398262539759609"; - let alphay = "2637289349983507610125993281171282870664683328789064436670091381805667870657250691837988574635646688089951719927247"; - - let betax1 = "1312620381151154625549413690218290437739613987001512553647554932245743783919690104921577716179019375920325686841943"; - let betax2 = "1853421227732662200477195678252233549930451033531229987959164216695698667330234953033341200627605777603511819497457"; - let betay1 = "3215807833988244618006117550809420301978856703407297742347804415291049013404133666905173282837707341742014140541018"; - let betay2 = "812366606879346135498483310623227330050424196838294715759414425317592599094348477520229174120664109186562798527696"; - - let gammax1 = "352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160"; - let gammax2 = "3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758"; - let gammay1 = "1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905"; - let gammay2 = "927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582"; - - let deltax1 = "2981843938988033214458466658185878126396080429969635248100956025957789319926032198626745120548947333202362392267114"; - let deltax2 = "2236695112259305382987038341098587500598216646308901956168137697892380899086228863246537938263638056666003066263342"; - let deltay1 = "717163810166643254871951856655865822196000925757284470845197358532703820821048809982340614428800986999944933231635"; - let deltay2 = "3496058064578305387608803828034117220735807855182872031001942587835768203820179263722136810383631418598310938506798"; - - let ic0x = "829685638389803071404995253486571779300247099942205634643821309129201420207693030476756893332812706176564514055395"; - let ic0y = "3455508165409829148751617737772894557887792278044850553785496869183933597103951941805834639972489587640583544390358"; - - let ic1x = "2645559270376031734407122278942646687260452979296081924477586893972449945444985371392950465676350735694002713633589"; - let ic1y = "2241039659097418315097403108596818813895651201896886552939297756980670248638746432560267634304593609165964274111037"; - - // Construct the verification key from the pre-computed components - let vk = VerificationKey { - alpha: g1_from_coords(&env, alphax, alphay), - beta: g2_from_coords(&env, betax1, betax2, betay1, betay2), - gamma: g2_from_coords(&env, gammax1, gammax2, gammay1, gammay2), - delta: g2_from_coords(&env, deltax1, deltax2, deltay1, deltay2), - ic: Vec::from_array( - &env, - [ - g1_from_coords(&env, ic0x, ic0y), - g1_from_coords(&env, ic1x, ic1y), - ], - ), - }; - - // Load proof components (copied from `data/proof.json`) - let pi_ax = "314442236668110257304682488877371582255161413673331360366570443799415414639292047869143313601702131653514009114222"; - let pi_ay = "2384632327855835824635705027009217874826122107057894594162233214798350178691568018290025994699762298534539543934607"; - let pi_bx1 = "428844167033934720609657613212495751617651348480870890908850335525890280786532876634895457032623422366474694342656"; - let pi_bx2 = "3083139526360252775789959298805261067575555607578161553873977966165446991459924053189383038704105379290158793353905"; - let pi_by1 = "1590919422794657666432683000821892403620510405626533455397042191265963587891653562867091397248216891852168698286910"; - let pi_by2 = "3617931039814164588401589536353142503544155307022467123698224064329647390280346725086550997337076315487486714327146"; - let pi_cx = "3052934797502613468327963344215392478880720823583493172692775426011388142569325036386650708808320216973179639719187"; - let pi_cy = "2028185281516938724429867827057869371578022471499780916652824405212207527699373814371051328341613972789943854539597"; - - // Construct the proof from the pre-computed components - let proof = Proof { - a: g1_from_coords(&env, &pi_ax, &pi_ay), - b: g2_from_coords(&env, &pi_bx1, &pi_bx2, &pi_by1, &pi_by2), - c: g1_from_coords(&env, &pi_cx, &pi_cy), - }; - - // Create the contract client - let client = create_client(&env); - - // Test Case 1: Verify the proof with the correct public output (33, copied from `data/public.json`) - let output = Vec::from_array(&env, [Fr::from_u256(U256::from_u32(&env, 33))]); - let res = client.verify_proof(&vk, &proof, &output); - assert_eq!(res, true); - - // Print out the budget report showing CPU and memory cost breakdown for - // different operations (zero-value operations omitted for brevity) - env.cost_estimate().budget().print(); - /* - ================================================================= - Cpu limit: 100000000; used: 40968821 - Mem limit: 41943040; used: 297494 - ================================================================= - CostType cpu_insns mem_bytes - MemAlloc 12089 3401 - MemCpy 3091 0 - MemCmp 928 0 - VisitObject 5917 0 - ComputeSha256Hash 3738 0 - Bls12381EncodeFp 2644 0 - Bls12381DecodeFp 29550 0 - Bls12381G1CheckPointOnCurve 13538 0 - Bls12381G1CheckPointInSubgroup 3652550 0 - Bls12381G2CheckPointOnCurve 23684 0 - Bls12381G2CheckPointInSubgroup 4231288 0 - Bls12381G1ProjectiveToAffine 185284 0 - Bls12381G1Add 7689 0 - Bls12381G1Mul 2458985 0 - Bls12381Pairing 30335852 294093 - Bls12381FrFromU256 1994 0 - // ... zero-value rows omitted ... - ================================================================= - */ - - // Test Case 2: Verify the proof with an incorrect public output (22) - let output = Vec::from_array(&env, [Fr::from_u256(U256::from_u32(&env, 22))]); - let res = client.verify_proof(&vk, &proof, &output); - assert_eq!(res, false); -}