From fdb82bc119408b840ad3bdae6604c9d1cb90a72c Mon Sep 17 00:00:00 2001 From: nixw <> Date: Sun, 4 Feb 2024 17:03:23 +0200 Subject: [PATCH 1/5] Add verifier --- CMakeLists.txt | 4 +- depends/ffiasm | 2 +- depends/pistache | 2 +- src/CMakeLists.txt | 8 + src/fileloader.hpp | 2 + src/groth16.cpp | 440 ++++++++++++++++++++++++++++++++++++++++++ src/groth16.hpp | 69 +++++++ src/main_verifier.cpp | 56 ++++++ src/verifier.cpp | 135 +++++++++++++ src/verifier.h | 34 ++++ 10 files changed, 748 insertions(+), 4 deletions(-) create mode 100644 src/main_verifier.cpp create mode 100644 src/verifier.cpp create mode 100644 src/verifier.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a560fd0..8b0f066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ endif() add_subdirectory(src) -install(TARGETS prover rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_prover fr fq +install(TARGETS prover verifier rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_prover fr fq RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX}/app LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) @@ -61,5 +61,5 @@ install(TARGETS prover rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_pro install(FILES "${GMP_LIB_DIR}/${GMP_LIB_FILE}" DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) -install(FILES src/prover.h +install(FILES src/prover.h src/verifier.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/depends/ffiasm b/depends/ffiasm index f47c1cd..ebd87e6 160000 --- a/depends/ffiasm +++ b/depends/ffiasm @@ -1 +1 @@ -Subproject commit f47c1cd603b97cfe5c4a38720cf0b01f4120b0d6 +Subproject commit ebd87e6ed5920ca79ff25087577723a5109cadc1 diff --git a/depends/pistache b/depends/pistache index ae073a0..ae0da38 160000 --- a/depends/pistache +++ b/depends/pistache @@ -1 +1 @@ -Subproject commit ae073a0709ed1d6f0c28db90766c64b06f0366e6 +Subproject commit ae0da38cf1c26c3321b19a512e2faffb80ae9867 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0313a22..a183291 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,8 @@ set(LIB_SOURCES fileloader.hpp prover.cpp prover.h + verifier.cpp + verifier.h ../depends/ffiasm/c/misc.cpp ../depends/ffiasm/c/naf.cpp ../depends/ffiasm/c/splitparstr.cpp @@ -115,10 +117,14 @@ set_target_properties(rapidsnarkStaticFrFq PROPERTIES OUTPUT_NAME rapidsnark-fr- add_executable(prover main_prover.cpp) target_link_libraries(prover rapidsnarkStatic) +add_executable(verifier main_verifier.cpp) +target_link_libraries(verifier rapidsnarkStatic) + add_library(rapidsnark SHARED ${LIB_SOURCES}) if(USE_LOGGER OR NOT USE_OPENMP) target_link_libraries(prover pthread) + target_link_libraries(verifier pthread) endif() if(USE_SODIUM) @@ -130,10 +136,12 @@ if(OpenMP_CXX_FOUND) if(TARGET_PLATFORM MATCHES "android") target_link_libraries(prover -static-openmp -fopenmp) + target_link_libraries(verifier -static-openmp -fopenmp) target_link_libraries(rapidsnark -static-openmp -fopenmp) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(prover OpenMP::OpenMP_CXX) + target_link_libraries(verifier OpenMP::OpenMP_CXX) endif() endif() diff --git a/src/fileloader.hpp b/src/fileloader.hpp index e1ac663..831d61b 100644 --- a/src/fileloader.hpp +++ b/src/fileloader.hpp @@ -15,6 +15,8 @@ class FileLoader void* dataBuffer() { return addr; } size_t dataSize() const { return size; } + std::string dataAsString() { return std::string((char*)addr, size); } + private: void* addr; size_t size; diff --git a/src/groth16.cpp b/src/groth16.cpp index fd21991..c25a51e 100644 --- a/src/groth16.cpp +++ b/src/groth16.cpp @@ -338,4 +338,444 @@ json Proof::toJson() { return p; } +template +static void +G1PointAffineFromJson(Engine &E, typename Engine::G1PointAffine &point, const json &value) +{ + E.f1.fromString(point.x, value[0]); + E.f1.fromString(point.y, value[1]); +} + +template +static void +G2PointAffineFromJson(Engine &E, typename Engine::G2PointAffine &point, const json &value) +{ + E.f1.fromString(point.x.a, value[0][0]); + E.f1.fromString(point.x.b, value[0][1]); + E.f1.fromString(point.y.a, value[1][0]); + E.f1.fromString(point.y.b, value[1][1]); +} + +template +void Proof::fromJson(const json& proof) +{ + G1PointAffineFromJson(E, A, proof["pi_a"]); + G2PointAffineFromJson(E, B, proof["pi_b"]); + G1PointAffineFromJson(E, C, proof["pi_c"]); +} + +template +void VerificationKey::fromJson(const json& key) +{ + G1PointAffineFromJson(E, Alpha, key["vk_alpha_1"]); + G2PointAffineFromJson(E, Beta, key["vk_beta_2"]); + G2PointAffineFromJson(E, Gamma, key["vk_gamma_2"]); + G2PointAffineFromJson(E, Delta, key["vk_delta_2"]); + + auto j_ic = key["IC"]; + + IC.reserve(j_ic.size()); + + for (const auto& el : j_ic.items()) { + typename Engine::G1PointAffine point; + + G1PointAffineFromJson(E, point, el.value()); + IC.push_back(point); + } +} + +template +Verifier::Verifier() + : E(Engine::engine) +{ + E.f2.fromString(xiToPMinus1Over3, + "10307601595873709700152284273816112264069230130616436755625194854815875713954," + "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + + E.f2.fromString(xiToPMinus1Over2, + "10307601595873709700152284273816112264069230130616436755625194854815875713954," + "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + + E.f1.fromString(xiToPSquaredMinus1Over3, + "21888242871839275220042445260109153167277707414472061641714758635765020556616"); +} + +template +bool Verifier::verify(Proof &proof, InputsVector &inputs, + VerificationKey &key) +{ + if (inputs.size() + 1 != key.IC.size()) { + throw std::invalid_argument("len(inputs)+1 != len(vk.IC)"); + } + + typename Engine::G1Point vkX = E.g1.zero(); + + for (int i = 0; i < inputs.size(); i++) { + typename Engine::FrElement input; + + E.fr.fromMontgomery(input, inputs[i]); + + typename Engine::G1Point p1; + E.g1.mulByScalar(p1, key.IC[i+1], (uint8_t *)&input, sizeof(input)); + E.g1.add(vkX, vkX, p1); + } + + E.g1.add(vkX, vkX, key.IC[0]); + + typename Engine::G1Point pA; + E.g1.copy(pA, proof.A); + + typename Engine::G1Point negAlpha; + E.g1.neg(negAlpha, key.Alpha); + + typename Engine::G1Point negvkX; + E.g1.neg(negvkX, vkX); + + typename Engine::G1Point negC; + E.g1.neg(negC, proof.C); + + typename Engine::G2Point pB; + E.g2.copy(pB, proof.B); + + typename Engine::G2Point pBeta; + E.g2.copy(pBeta, key.Beta); + + typename Engine::G2Point pGamma; + E.g2.copy(pGamma, key.Gamma); + + typename Engine::G2Point pDelta; + E.g2.copy(pDelta, key.Delta); + + G1PointArray g1 = {pA, negAlpha, negvkX, negC}; + G2PointArray g2 = {pB, pBeta, pGamma, pDelta}; + + return pairingCheck(g1, g2); +} + +template +void Verifier::lineFunctionAdd( + typename Engine::G2Point& r, + typename Engine::G2PointAffine& p, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& r2, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut) +{ + typename Engine::F2Element B, D, H, I, E1, J, L1, V, t, t2; + + E.f2.mul(B, p.x, r.zzz); + E.f2.mul(D, p.y, r.zz); + E.f2.square(D, D); + E.f2.sub(D, D, r2); + E.f2.sub(D, D, r.zzz); + E.f2.mul(D, D, r.zzz); + + E.f2.sub(H, B, r.x); + E.f2.square(I, H); + E.f2.add(E1, I, I); + E.f2.add(E1, E1, E1); + E.f2.mul(J, H, E1); + E.f2.sub(L1, D, r.y); + E.f2.sub(L1, L1, r.y); + E.f2.mul(V, r.x, E1); + + E.f2.square(rOut.y, L1); + E.f2.sub(rOut.x, rOut.x, J); + E.f2.sub(rOut.x, rOut.x, V); + E.f2.sub(rOut.x, rOut.x, V); + + E.f2.add(rOut.zz, r.zz, H); + E.f2.square(rOut.zz, rOut.zz); + E.f2.sub(rOut.zz, rOut.zz, r.zzz); + E.f2.sub(rOut.zz, rOut.zz, I); + + E.f2.sub(t, V, rOut.x); + E.f2.mul(t, t, L1); + E.f2.mul(t2, r.y, J); + E.f2.add(t2, t2, t2); + E.f2.sub(rOut.y, t, t2); + E.f2.square(rOut.zzz, rOut.zz); + + E.f2.add(t, p.y, rOut.zz); + E.f2.square(t, t); + E.f2.sub(t, t, r2); + E.f2.sub(t, t, rOut.zzz); + + E.f2.mul(t2, L1, p.x); + E.f2.add(t2, t2, t2); + + E.f2.sub(a, t2, t); + + E.f2.mulScalar(c, rOut.zz, q.y); + E.f2.add(c, c, c); + + E.f2.copy(b, E.f2.zero()); + E.f2.sub(b, b, L1); + E.f2.mulScalar(b, b, q.x); + E.f2.add(b, b, b); +} + +template +void Verifier::lineFunctionDouble( + typename Engine::G2Point& r, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut) +{ + typename Engine::F2Element A, B, C_, D, E1, G, t; + + E.f2.square(A, r.x); + E.f2.square(B, r.y); + E.f2.square(C_, B); + E.f2.add(D, r.x, B); + E.f2.square(D, D); + E.f2.sub(D, D, A); + E.f2.sub(D, D, C_); + E.f2.add(D, D, D); + + E.f2.add(E1, A, A); + E.f2.square(G, E1); + E.f2.sub(rOut.x, G, D); + E.f2.sub(rOut.x, rOut.x, D); + + E.f2.add(rOut.zz, r.y, r.zz); + E.f2.square(rOut.zz, rOut.zz); + E.f2.sub(rOut.zz, rOut.zz, B); + E.f2.sub(rOut.zz, rOut.zz, r.zzz); + + E.f2.sub(rOut.y, D, rOut.x); + E.f2.mul(rOut.y, rOut.y, E1); + + E.f2.add(t, C_, C_); + E.f2.add(t, t, t); + E.f2.add(t, t, t); + E.f2.sub(rOut.y, rOut.y, t); + E.f2.square(rOut.zzz, rOut.zz); + + E.f2.mul(t, E1, r.zzz); + E.f2.add(t, t, t); + E.f2.copy(b, E.f2.zero()); + E.f2.sub(b, b, t); + E.f2.mulScalar(b, b, q.x); + + E.f2.add(a, r.x, E1); + E.f2.square(a, a); + E.f2.sub(a, a, A); + E.f2.sub(a, a, G); + E.f2.add(t, B, B); + E.f2.add(t, t, t); + E.f2.sub(a, a, t); + + E.f2.mul(c, rOut.zz, r.zzz); + E.f2.add(c, c, c); + E.f2.mulScalar(c, c, q.y); +} + +template +void Verifier::mulLine( + typename Engine::F12Element& ret, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c) +{ + typename Engine::F6Element a2, t3, t2; + typename Engine::F2Element t; + + E.f2.copy(a2.x, E.f2.zero()); + E.f2.copy(a2.y, a); + E.f2.copy(a2.z, b); + + E.f6.mul(a2, a2, ret.x); + E.f6.mulScalar(t3, ret.y, c); + + E.f2.add(t, b, c); + E.f2.copy(t2.x, E.f2.zero()); + E.f2.copy(t2.y, a); + E.f2.copy(t2.z, t); + E.f6.add(ret.x, ret.x, ret.y); + + E.f6.copy(ret.y, t3); + + E.f6.mul(ret.x, ret.x, t2); + E.f6.sub(ret.x, ret.x, a2); + E.f6.sub(ret.x, ret.x, ret.y); + E.f6.mulTau(a2, a2); + E.f6.add(ret.y, ret.y, a2); +} + +template +typename Engine::F12Element +Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& p) +{ + + const char sixuPlus2NAF[] = {0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, + 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, + 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, + 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}; + + typename Engine::F12Element ret = E.f12.one(); + + typename Engine::G2PointAffine aAffine, minusA; + typename Engine::G1PointAffine bAffine; + typename Engine::G2Point r, newR; + typename Engine::F2Element r2, a, b, c; + + E.g2.copy(aAffine, q); + E.g1.copy(bAffine, p); + E.g2.copy(minusA, aAffine); + E.g2.copy(r, aAffine); + + E.f2.square(r2, aAffine.y); + + const size_t count = sizeof(sixuPlus2NAF) - 1; + + for (int i = count; i > 0; i--) { + + lineFunctionDouble(r, bAffine, a, b, c, newR); + + if (i != count) { + E.f12.square(ret, ret); + } + + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + switch (sixuPlus2NAF[i-1]) { + case 1: + break; + lineFunctionAdd(r, aAffine, bAffine, r2, a, b, c, newR); + + case -1: + lineFunctionAdd(r, minusA, bAffine, r2, a, b, c, newR); + break; + + default: + continue; + } + + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + } + + typename Engine::G2Point q1; + + E.f2.conjugate(q1.x, aAffine.x); + E.f2.mul(q1.x, q1.x, xiToPMinus1Over3); + E.f2.conjugate(q1.y, aAffine.y); + E.f2.mul(q1.y, q1.y, xiToPMinus1Over2); + E.f2.copy(q1.zz, E.f2.one()); + E.f2.copy(q1.zzz, E.f2.one()); + + typename Engine::G2Point minusQ2; + + E.f2.copy(minusQ2.y, aAffine.y); + E.f2.copy(minusQ2.zz, E.f2.one()); + E.f2.copy(minusQ2.zzz, E.f2.one()); + E.f2.mulScalar(minusQ2.x, aAffine.x, xiToPSquaredMinus1Over3); + + E.f2.square(r2, q1.y); + + typename Engine::G2PointAffine q1Affine; + E.g2.copy(q1Affine, q1); + + lineFunctionAdd(r, q1Affine, bAffine, r2, a, b, c, newR); + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + typename Engine::G2PointAffine minusQ2Affine; + E.g2.copy(minusQ2Affine, minusQ2); + + E.f2.square(r2, minusQ2.y); + lineFunctionAdd(r, minusQ2Affine, bAffine, r2, a, b, c, newR); + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + return ret; +} + +template +typename Engine::F12Element +Verifier::finalExponentiation(typename Engine::F12Element& in) +{ + typename Engine::F12Element t1, inv, t2, fp, fp2, fp3, fu, fu2, fu3, y3, fu2p, fu3p; + typename Engine::F12Element y2, y0, y1, y4, y5, y6, t0; + + uint64_t u = 4965661367192848881; + + E.f6.neg(t1.x, in.x); + E.f6.copy(t1.y, in.y); + + E.f12.inv(inv, in); + E.f12.mul(t1, t1, inv); + + E.f12.FrobeniusP2(t2, t1); + E.f12.mul(t1, t1, t2); + + E.f12.Frobenius(fp, t1); + E.f12.FrobeniusP2(fp2, t1); + E.f12.Frobenius(fp3, fp2); + + E.f12.exp(fu, t1, (uint8_t*)&u, sizeof(u)); + E.f12.exp(fu2, fu, (uint8_t*)&u, sizeof(u)); + E.f12.exp(fu3, fu2, (uint8_t*)&u, sizeof(u)); + + E.f12.Frobenius(y3, fu); + E.f12.Frobenius(fu2p, fu2); + E.f12.Frobenius(fu3p, fu3); + E.f12.FrobeniusP2(y2, fu2); + + E.f12.mul(y0, fp, fp2); + E.f12.mul(y0, y0, fp3); + + E.f12.conjugate(y1, t1); + E.f12.conjugate(y5, fu2); + E.f12.conjugate(y3, y3); + E.f12.mul(y4, fu, fu2p); + E.f12.conjugate(y4, y4); + + E.f12.mul(y6, fu3, fu3p); + E.f12.conjugate(y6, y6); + + E.f12.square(t0, y6); + E.f12.mul(t0, t0, y4); + E.f12.mul(t0, t0, y5); + E.f12.mul(t1, y3, y5); + E.f12.mul(t1, t1, t0); + E.f12.mul(t0, t0, y2); + + E.f12.square(t1, t1); + E.f12.mul(t1, t1, t0); + E.f12.square(t1, t1); + E.f12.mul(t0, t1, y1); + E.f12.mul(t1, t1, y0); + E.f12.square(t0, t0); + E.f12.mul(t0, t0, t1); + + return t0; +} + +template +bool Verifier::pairingCheck(G1PointArray& a, G2PointArray& b) +{ + typename Engine::F12Element acc = E.f12.one(); + + for (int i = 0; i < a.size(); i++) { + + if (E.g1.isZero(a[i]) || E.g2.isZero(b[i])) { + continue; + + auto millerRes = miller(b[i], a[i]); + E.f12.mul(acc, acc, millerRes); + } + } + + auto ret = finalExponentiation(acc); + + return E.f12.isOne(ret); +} + } // namespace diff --git a/src/groth16.hpp b/src/groth16.hpp index 396a1ab..c113491 100644 --- a/src/groth16.hpp +++ b/src/groth16.hpp @@ -2,6 +2,7 @@ #define GROTH16_HPP #include +#include #include using json = nlohmann::json; @@ -20,8 +21,22 @@ namespace Groth16 { Proof(Engine &_E) : E(_E) { } std::string toJsonStr(); json toJson(); + void fromJson(const json& proof); }; + template + class VerificationKey { + Engine &E; + public: + typename Engine::G1PointAffine Alpha; + typename Engine::G2PointAffine Beta; + typename Engine::G2PointAffine Gamma; + typename Engine::G2PointAffine Delta; + std::vector IC; + + VerificationKey(Engine &_E) : E(_E) { } + void fromJson(const json& proof); + }; #pragma pack(push, 1) template @@ -118,6 +133,60 @@ namespace Groth16 { void *pointsC, void *pointsH ); + + template + class Verifier { + + typedef std::vector InputsVector; + typedef std::array G1PointArray; + typedef std::array G2PointArray; + + Engine &E; + + public: + Verifier(); + + bool verify( + Proof &proof, + InputsVector &inputs, + VerificationKey &key); + + private: + bool pairingCheck(G1PointArray& g1, G2PointArray& g2); + + typename Engine::F12Element miller(typename Engine::G2Point& b, typename Engine::G1Point& a); + + typename Engine::F12Element finalExponentiation(typename Engine::F12Element& in); + + void lineFunctionDouble( + typename Engine::G2Point& r, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut); + + void lineFunctionAdd( + typename Engine::G2Point& r, + typename Engine::G2PointAffine& p, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& r2, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut); + + void mulLine( + typename Engine::F12Element& ret, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c); + + private: + typename Engine::F2Element xiToPMinus1Over3; + typename Engine::F2Element xiToPMinus1Over2; + typename Engine::F1Element xiToPSquaredMinus1Over3; + }; } diff --git a/src/main_verifier.cpp b/src/main_verifier.cpp new file mode 100644 index 0000000..d08f310 --- /dev/null +++ b/src/main_verifier.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include "fileloader.hpp" +#include "verifier.h" + +int main(int argc, char **argv) +{ + if (argc != 4) { + std::cerr << "Invalid number of parameters:\n"; + std::cerr << "Usage: verifier \n"; + return EXIT_FAILURE; + } + + try { + const std::string proofFilename = argv[1]; + const std::string inputsFilename = argv[2]; + const std::string keyFilename = argv[3]; + + BinFileUtils::FileLoader proof(proofFilename); + BinFileUtils::FileLoader inputs(inputsFilename); + BinFileUtils::FileLoader key(keyFilename); + + char errorMessage[256]; + + const int error = groth16_verify(proof.dataAsString().c_str(), + inputs.dataAsString().c_str(), + key.dataAsString().c_str(), + errorMessage, sizeof(errorMessage)); + + if (error == VERIFIER_VALID_PROOF) { + + std::cerr << "Result: Valid proof" << std::endl; + return EXIT_SUCCESS; + + } else if (error == VERIFIER_INVALID_PROOF) { + + std::cerr << "Result: Invalid proof" << std::endl; + return EXIT_FAILURE; + + } else { + std::cerr << "Error: " << errorMessage << '\n'; + return EXIT_FAILURE; + } + + } catch (std::exception* e) { + std::cerr << "Error: " << e->what() << std::endl; + return EXIT_FAILURE; + + } catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_FAILURE; +} diff --git a/src/verifier.cpp b/src/verifier.cpp new file mode 100644 index 0000000..0f55f5e --- /dev/null +++ b/src/verifier.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +#include "verifier.h" +#include "groth16.hpp" + +using json = nlohmann::json; + +static Groth16::Proof +parse_proof(const char *proof_str) +{ + Groth16::Proof proof(AltBn128::Engine::engine); + + try { + json proof_json = json::parse(proof_str); + + std::string protocol = proof_json["protocol"].template get(); + + if (protocol != "groth16") { + throw std::invalid_argument("invalid proof data"); + } + + proof.fromJson(proof_json); + + } catch(...) { + throw std::invalid_argument("invalid proof data") ; + } + + return proof; +} + +static std::vector +parse_inputs(const char *inputs_str) +{ + std::vector inputs; + + try { + json inputs_json = json::parse(inputs_str); + + auto inputs_str_vec = inputs_json.template get>(); + + if (inputs_str_vec.empty()) { + throw std::invalid_argument("invalid inputs data"); + } + + inputs.reserve(inputs_str_vec.size()); + + for (const auto& elem: inputs_str_vec) { + AltBn128::FrElement aux; + + AltBn128::Fr.fromString(aux, elem); + inputs.push_back(aux); + } + + } catch(...) { + throw std::invalid_argument("invalid inputs data") ; + } + + return inputs; +} + +static Groth16::VerificationKey +parse_key(const char *key_str) +{ + Groth16::VerificationKey key(AltBn128::Engine::engine); + + try { + json key_json = json::parse(key_str); + + auto protocol = key_json["protocol"].template get(); + auto curve = key_json["curve"].template get(); + auto nPublic = key_json["nPublic"].template get(); + + if (protocol != "groth16" || curve != "bn128") { + throw std::invalid_argument("invalid verification key data"); + } + + key.fromJson(key_json); + + if (key.IC.empty()) { + throw std::invalid_argument("invalid verification key data"); + } + + } catch(...) { + throw std::invalid_argument("invalid verification key data"); + } + + return key; +} + +int +groth16_verify(const char *proof, + const char *inputs, + const char *verification_key, + char *error_msg, + unsigned long error_msg_maxsize) +{ + try { + + auto proof_value = parse_proof(proof); + auto inputs_value = parse_inputs(inputs); + auto key_value = parse_key(verification_key); + + Groth16::Verifier verifier; + + bool valid = verifier.verify(proof_value, inputs_value, key_value); + + return valid ? VERIFIER_VALID_PROOF : VERIFIER_INVALID_PROOF; + + } catch (std::exception& e) { + + if (error_msg) { + strncpy(error_msg, e.what(), error_msg_maxsize); + } + return VERIFIER_ERROR; + + } catch (std::exception *e) { + + if (error_msg) { + strncpy(error_msg, e->what(), error_msg_maxsize); + } + delete e; + return VERIFIER_ERROR; + + } catch (...) { + if (error_msg) { + strncpy(error_msg, "unknown error", error_msg_maxsize); + } + return VERIFIER_ERROR; + } + + return VERIFIER_INVALID_PROOF; +} diff --git a/src/verifier.h b/src/verifier.h new file mode 100644 index 0000000..5e684c5 --- /dev/null +++ b/src/verifier.h @@ -0,0 +1,34 @@ +#ifndef VERIFIER_HPP +#define VERIFIER_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +//Error codes returned by the functions. +#define VERIFIER_VALID_PROOF 0x0 +#define VERIFIER_INVALID_PROOF 0x1 +#define VERIFIER_ERROR 0x2 + +/** + * 'proof', 'inputs' and 'verification_key' are null-terminated json strings. + * + * @return error code: + * VERIFIER_VALID_PROOF - in case of valid 'proof'. + * VERIFIER_INVALID_PROOF - in case of invalid 'proof'. + VERIFIER_ERROR - in case of an error + */ + +int +groth16_verify(const char *proof, + const char *inputs, + const char *verification_key, + char *error_msg, + unsigned long error_msg_maxsize); + +#ifdef __cplusplus +} +#endif + + +#endif // VERIFIER_HPP From dd4f23bcdf8a3fbec55748aeb6ab79f172eaa290 Mon Sep 17 00:00:00 2001 From: nixw <> Date: Mon, 5 Feb 2024 23:52:36 +0200 Subject: [PATCH 2/5] Change type for sixuPlus2NAF --- src/groth16.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groth16.cpp b/src/groth16.cpp index c25a51e..96f67e5 100644 --- a/src/groth16.cpp +++ b/src/groth16.cpp @@ -612,7 +612,7 @@ typename Engine::F12Element Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& p) { - const char sixuPlus2NAF[] = {0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, + const int sixuPlus2NAF[] = {0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}; From 1557e85d7bd0c8850c952eb24e6a867b0448401c Mon Sep 17 00:00:00 2001 From: nixw <> Date: Tue, 13 Feb 2024 01:09:36 +0200 Subject: [PATCH 3/5] Change verifer argument order --- src/main_verifier.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main_verifier.cpp b/src/main_verifier.cpp index d08f310..8282919 100644 --- a/src/main_verifier.cpp +++ b/src/main_verifier.cpp @@ -8,14 +8,14 @@ int main(int argc, char **argv) { if (argc != 4) { std::cerr << "Invalid number of parameters:\n"; - std::cerr << "Usage: verifier \n"; + std::cerr << "Usage: verifier \n"; return EXIT_FAILURE; } try { - const std::string proofFilename = argv[1]; + const std::string keyFilename = argv[1]; const std::string inputsFilename = argv[2]; - const std::string keyFilename = argv[3]; + const std::string proofFilename = argv[3]; BinFileUtils::FileLoader proof(proofFilename); BinFileUtils::FileLoader inputs(inputsFilename); From c6ab1dc88dc7ceaead97e9f3fd3f2ad135034aa3 Mon Sep 17 00:00:00 2001 From: nixw <> Date: Fri, 16 Feb 2024 22:26:17 +0200 Subject: [PATCH 4/5] Fixes in verifier --- depends/ffiasm | 2 +- src/groth16.cpp | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/depends/ffiasm b/depends/ffiasm index ebd87e6..6593fed 160000 --- a/depends/ffiasm +++ b/depends/ffiasm @@ -1 +1 @@ -Subproject commit ebd87e6ed5920ca79ff25087577723a5109cadc1 +Subproject commit 6593fedbf183ebbaa62cbb295963ee8765f89fb0 diff --git a/src/groth16.cpp b/src/groth16.cpp index 96f67e5..e7eda1f 100644 --- a/src/groth16.cpp +++ b/src/groth16.cpp @@ -389,12 +389,12 @@ Verifier::Verifier() : E(Engine::engine) { E.f2.fromString(xiToPMinus1Over3, - "10307601595873709700152284273816112264069230130616436755625194854815875713954," - "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + "21575463638280843010398324269430826099269044274347216827212613867836435027261," + "10307601595873709700152284273816112264069230130616436755625194854815875713954"); E.f2.fromString(xiToPMinus1Over2, - "10307601595873709700152284273816112264069230130616436755625194854815875713954," - "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + "2821565182194536844548159561693502659359617185244120367078079554186484126554," + "3505843767911556378687030309984248845540243509899259641013678093033130930403"); E.f1.fromString(xiToPSquaredMinus1Over3, "21888242871839275220042445260109153167277707414472061641714758635765020556616"); @@ -466,7 +466,7 @@ void Verifier::lineFunctionAdd( typename Engine::F2Element B, D, H, I, E1, J, L1, V, t, t2; E.f2.mul(B, p.x, r.zzz); - E.f2.mul(D, p.y, r.zz); + E.f2.add(D, p.y, r.zz); E.f2.square(D, D); E.f2.sub(D, D, r2); E.f2.sub(D, D, r.zzz); @@ -481,7 +481,7 @@ void Verifier::lineFunctionAdd( E.f2.sub(L1, L1, r.y); E.f2.mul(V, r.x, E1); - E.f2.square(rOut.y, L1); + E.f2.square(rOut.x, L1); E.f2.sub(rOut.x, rOut.x, J); E.f2.sub(rOut.x, rOut.x, V); E.f2.sub(rOut.x, rOut.x, V); @@ -538,6 +538,7 @@ void Verifier::lineFunctionDouble( E.f2.add(D, D, D); E.f2.add(E1, A, A); + E.f2.add(E1, E1, A); E.f2.square(G, E1); E.f2.sub(rOut.x, G, D); E.f2.sub(rOut.x, rOut.x, D); @@ -626,12 +627,12 @@ Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& E.g2.copy(aAffine, q); E.g1.copy(bAffine, p); - E.g2.copy(minusA, aAffine); + E.g2.neg(minusA, aAffine); E.g2.copy(r, aAffine); E.f2.square(r2, aAffine.y); - const size_t count = sizeof(sixuPlus2NAF) - 1; + const size_t count = sizeof(sixuPlus2NAF)/sizeof(sixuPlus2NAF[0]) - 1; for (int i = count; i > 0; i--) { @@ -646,9 +647,8 @@ Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& switch (sixuPlus2NAF[i-1]) { case 1: - break; lineFunctionAdd(r, aAffine, bAffine, r2, a, b, c, newR); - + break; case -1: lineFunctionAdd(r, minusA, bAffine, r2, a, b, c, newR); break; @@ -672,10 +672,10 @@ Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& typename Engine::G2Point minusQ2; + E.f2.mulScalar(minusQ2.x, aAffine.x, xiToPSquaredMinus1Over3); E.f2.copy(minusQ2.y, aAffine.y); E.f2.copy(minusQ2.zz, E.f2.one()); E.f2.copy(minusQ2.zzz, E.f2.one()); - E.f2.mulScalar(minusQ2.x, aAffine.x, xiToPSquaredMinus1Over3); E.f2.square(r2, q1.y); @@ -767,10 +767,10 @@ bool Verifier::pairingCheck(G1PointArray& a, G2PointArray& b) if (E.g1.isZero(a[i]) || E.g2.isZero(b[i])) { continue; - - auto millerRes = miller(b[i], a[i]); - E.f12.mul(acc, acc, millerRes); } + + auto millerRes = miller(b[i], a[i]); + E.f12.mul(acc, acc, millerRes); } auto ret = finalExponentiation(acc); From 233a628927818e45c2046c0062896149e3775f79 Mon Sep 17 00:00:00 2001 From: Oleh Lomaka Date: Fri, 23 Feb 2024 03:12:03 -0500 Subject: [PATCH 5/5] Add integration tests to check verifier for correct and incorrect inputs. Add unit tests for functions groth16_public_size_for_zkey_file and groth16_public_size_for_zkey_buf. Also enable ctest functionality that would enable us to write unit tests for different parts and run them as part for the cmake pipeline. --- .github/workflows/build.yml | 39 +++++++--- CMakeLists.txt | 4 +- README.md | 19 +++++ src/CMakeLists.txt | 7 ++ src/test_public_size.c | 145 ++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 src/test_public_size.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f60650..01b9b26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,14 +56,21 @@ jobs: mkdir -p build_prover && cd build_prover cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package make -j4 && make install + ctest --rerun-failed --output-on-failure - - name: test prover for linux + - name: test rapidsnark run: | set -x set -e - npm install -g snarkjs package/bin/prover testdata/circuit_final.zkey testdata/witness.wtns proof.json public.json - snarkjs groth16 verify testdata/verification_key.json public.json proof.json + package/bin/verifier testdata/verification_key.json public.json proof.json + # make a wrong public.json by decrementing the first element by 1 + (value_0=$(jq '.[0]' public.json | tr -d '"') && value_0=$(echo "$value_0 - 1" | BC_LINE_LENGTH=100 bc) && jq --arg value_0 "$value_0" '.[0] = $value_0' public.json) > public_bad.json + set +e + package/bin/verifier testdata/verification_key.json public_bad.json proof.json + exit_code=$? + set -e + [ $exit_code -ne 0 ] - name: upload Linux amd64 dev artifacts if: github.event_name != 'release' @@ -161,14 +168,21 @@ jobs: mkdir -p build_prover_macos_arm64 && cd build_prover_macos_arm64 cmake .. -DTARGET_PLATFORM=macos_arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_macos_arm64 make -j4 && make install + ctest --rerun-failed --output-on-failure - - name: test prover + - name: test rapidsnark run: | set -x set -e - npm install -g snarkjs package_macos_arm64/bin/prover testdata/circuit_final.zkey testdata/witness.wtns proof.json public.json - snarkjs groth16 verify testdata/verification_key.json public.json proof.json + package_macos_arm64/bin/verifier testdata/verification_key.json public.json proof.json + # make a wrong public.json by decrementing the first element by 1 + (value_0=$(jq '.[0]' public.json | tr -d '"') && value_0=$(echo "$value_0 - 1" | BC_LINE_LENGTH=100 bc) && jq --arg value_0 "$value_0" '.[0] = $value_0' public.json) > public_bad.json + set +e + package_macos_arm64/bin/verifier testdata/verification_key.json public_bad.json proof.json + exit_code=$? + set -e + [ $exit_code -ne 0 ] - name: upload macOS arm64 dev artifacts if: github.event_name != 'release' @@ -256,14 +270,21 @@ jobs: mkdir -p build_prover_macos_x86_64 && cd build_prover_macos_x86_64 cmake .. -DTARGET_PLATFORM=macos_x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_macos_x86_64 make -j4 && make install + ctest --rerun-failed --output-on-failure - - name: test prover + - name: test rapidsnark run: | set -x set -e - npm install -g snarkjs package_macos_x86_64/bin/prover testdata/circuit_final.zkey testdata/witness.wtns proof.json public.json - snarkjs groth16 verify testdata/verification_key.json public.json proof.json + package_macos_x86_64/bin/verifier testdata/verification_key.json public.json proof.json + # make a wrong public.json by decrementing the first element by 1 + (value_0=$(jq '.[0]' public.json | tr -d '"') && value_0=$(echo "$value_0 - 1" | BC_LINE_LENGTH=100 bc) && jq --arg value_0 "$value_0" '.[0] = $value_0' public.json) > public_bad.json + set +e + package_macos_x86_64/bin/verifier testdata/verification_key.json public_bad.json proof.json + exit_code=$? + set -e + [ $exit_code -ne 0 ] - name: upload macOS x86_64 dev artifacts if: github.event_name != 'release' diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b0f066..e982464 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(cmake/platform.cmake) set(USE_ASM ON CACHE BOOL "Use asm implementation for Fr and Fq") set(USE_OPENMP ON CACHE BOOL "Use OpenMP") -project(rapidsnark LANGUAGES CXX ASM) +project(rapidsnark LANGUAGES CXX C ASM) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -63,3 +63,5 @@ install(FILES "${GMP_LIB_DIR}/${GMP_LIB_FILE}" install(FILES src/prover.h src/verifier.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) + +enable_testing() diff --git a/README.md b/README.md index 4288bd5..5abd42a 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,25 @@ The prover is much faster that snarkjs and faster than bellman. [TODO] Some comparative tests should be done. +## Run tests + +You need to perform all the steps from the +[Compile prover in standalone mode](#compile-prover-in-standalone-mode) section. +After that you can run tests with the following command from the build +directory: + +```sh +# Make sure you are in the build directory +# ./build_prover for linux, ./build_prover_macos_arm64 for macOS. +cmake --build . --parallel && ctest --rerun-failed --output-on-failure +``` + +To run just the `test_public_size` test for custom zkey to measure the +performance, you can run the following command from the build directory: + +```sh +src/test_public_size ../testdata/circuit_final.zkey 86 +``` ## License diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a183291..2714cba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,12 @@ if(USE_SODIUM) endif() +enable_testing() +add_executable(test_public_size test_public_size.c) +target_link_libraries(test_public_size rapidsnarkStaticFrFq) +add_test(NAME test_public_size COMMAND test_public_size circuit_final.zkey 86 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdata) + if(OpenMP_CXX_FOUND) if(TARGET_PLATFORM MATCHES "android") @@ -142,6 +148,7 @@ if(OpenMP_CXX_FOUND) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(prover OpenMP::OpenMP_CXX) target_link_libraries(verifier OpenMP::OpenMP_CXX) + target_link_libraries(test_public_size OpenMP::OpenMP_CXX) endif() endif() diff --git a/src/test_public_size.c b/src/test_public_size.c new file mode 100644 index 0000000..d5aebe4 --- /dev/null +++ b/src/test_public_size.c @@ -0,0 +1,145 @@ +/** + * Test functions groth16_public_size_for_zkey_file and + * groth16_public_size_for_zkey_buf. + * + * Run it as + * ./test_public_size + * it will calculate the required public signals buffer size from the zkey file + * and compare it with the expected one. Return 0 if success. + * Return 1 if failure. Also prints the time taken by each function. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "prover.h" + +int +test_groth16_public_size(const char *zkey_fname, size_t *public_size) { + int ret_val = 0; + const int error_sz = 256; + char error_msg[error_sz]; + + int fd = open(zkey_fname, O_RDONLY); + if (fd == -1) { + printf("Error: %s\n", "open"); + ret_val = 1; + goto cleanup; + } + + struct stat sb = {0}; + if (fstat(fd, &sb) == -1) { + printf("Error: %s\n", "fstat"); + ret_val = 1; + goto cleanup; + } + + void *buf = malloc(sb.st_size); + if (buf == NULL) { + printf("Error: %s\n", "malloc"); + ret_val = 1; + goto cleanup; + } + + ssize_t bytes_read = read(fd, buf, sb.st_size); + if (bytes_read != sb.st_size) { + printf("Error: %s\n", "read"); + ret_val = 1; + goto cleanup; + } + + int ok = groth16_public_size_for_zkey_buf(buf, sb.st_size, public_size, error_msg, error_sz); + if (ok == 0) { + printf("Public size: %lu\n", *public_size); + } else { + printf("Error: %s\n", error_msg); + ret_val = 1; + goto cleanup; + } + +cleanup: + + if (fd != -1) + if (close(fd) == -1) + printf("Error: %s\n", "close"); + + return ret_val; +} + +int +test_groth16_public_size_for_zkey_file(const char *zkey_fname, + size_t *public_size) { + const int err_ln = 256; + char error_msg[err_ln]; + int ret = groth16_public_size_for_zkey_file(zkey_fname, public_size, error_msg, err_ln); + + if (ret == 0) { + printf("Public size: %lu\n", *public_size); + } else { + printf("Error: %s\n", error_msg); + } + return ret; +} + +int +main(int argc, char *argv[]) { + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + long want_pub_size = 0; + want_pub_size = strtol(argv[2], NULL, 10); + + int ret_val = 0; + clock_t start = clock(); + + size_t public_size = 0; + + int test_groth16_public_size_ok = + test_groth16_public_size(argv[1], &public_size); + if (test_groth16_public_size_ok) { + printf("test_groth16_public_size failed\n"); + ret_val = 1; + } else { + clock_t end = clock(); + printf("test_groth16_public_size succeeded in %f seconds\n", + (double)(end - start) / CLOCKS_PER_SEC); + } + + if (public_size != want_pub_size) { + printf("test_groth16_public_size expected public signals buf size: %ld\n", + want_pub_size); + printf("test_groth16_public_size actual public signals buf size: %lu\n", + public_size); + ret_val = 1; + } + + public_size = 0; + start = clock(); + int test_groth16_public_size_for_zkey_file_ok = + test_groth16_public_size_for_zkey_file(argv[1], &public_size); + if (test_groth16_public_size_for_zkey_file_ok) { + printf("test_groth16_public_size_for_zkey_file failed\n"); + ret_val = 1; + } else { + clock_t end = clock(); + printf("test_groth16_public_size_for_zkey_file succeeded in %f seconds\n", + (double)(end - start) / CLOCKS_PER_SEC); + } + + if (public_size != want_pub_size) { + printf("test_groth16_public_size_for_zkey_file expected public signals buf size: %ld\n", + want_pub_size); + printf("test_groth16_public_size_for_zkey_file actual public signals buf size: %lu\n", + public_size); + ret_val = 1; + } + + return ret_val; +} +