博客
关于我
Circom and Snarkjs,基于以太坊的零知识证明实现
阅读量:285 次
发布时间:2019-03-01

本文共 13988 字,大约阅读时间需要 46 分钟。

Circom is a DSL language to create generic statments (circuits) to be used in zero knowledge proofs. Snarkjs is an alternative implementation of zkSnarks Pinoccio and Groth16 protocols. It allows to generate proofs and verify them offchain and in the Ethereum chain.

中有大量例子。

在这里插入图片描述

是对zkSnarks(BN128曲线)的Pinoccio和Groth16协议的实现(–protocol groth)。

在这里插入图片描述

可参照进行体验。基本的流程遵循:

在这里插入图片描述

Snarkjs可借鉴学习的地方:

1)支持offchain和onchain验证;
2)其中的calculatewitnessprintconstraints很直观,值得学习;
3)generateverifier生成的verifier.sol智能合约,是基于《Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture》论文Page-25页的算法实现。

//// Copyright 2017 Christian Reitwiessner// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.pragma solidity ^0.4.17;library Pairing {    struct G1Point {        uint X;        uint Y;    }    // Encoding of field elements is: X[0] * z + X[1]    struct G2Point {        uint[2] X;        uint[2] Y;    }    /// @return the generator of G1    function P1() pure internal returns (G1Point) {        return G1Point(1, 2);    }    /// @return the generator of G2    function P2() pure internal returns (G2Point) {        // Original code point        return G2Point(            [11559732032986387107991004021392285783925812861821192530917403151452391805634,             10857046999023057135944570762232829481370756359578518086990519993285655852781],            [4082367875863433681332203403145435568316851327593401208105741076214120093531,             8495653923123431417604973247489272438418190587263600148770280649306958101930]        );/*        // Changed by Jordi point        return G2Point(            [10857046999023057135944570762232829481370756359578518086990519993285655852781,             11559732032986387107991004021392285783925812861821192530917403151452391805634],            [8495653923123431417604973247489272438418190587263600148770280649306958101930,             4082367875863433681332203403145435568316851327593401208105741076214120093531]        );*/    }    /// @return the negation of p, i.e. p.addition(p.negate()) should be zero.    function negate(G1Point p) pure internal returns (G1Point) {        // The prime q in the base field F_q for G1        uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;        if (p.X == 0 && p.Y == 0)            return G1Point(0, 0);        return G1Point(p.X, q - (p.Y % q));    }    /// @return the sum of two points of G1    function addition(G1Point p1, G1Point p2) view internal returns (G1Point r) {        uint[4] memory input;        input[0] = p1.X;        input[1] = p1.Y;        input[2] = p2.X;        input[3] = p2.Y;        bool success;        assembly {            success := staticcall(sub(gas, 2000), 6, input, 0xc0, r, 0x60)            // Use "invalid" to make gas estimation work            switch success case 0 { invalid() }        }        require(success);    }    /// @return the product of a point on G1 and a scalar, i.e.    /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.    function scalar_mul(G1Point p, uint s) view internal returns (G1Point r) {        uint[3] memory input;        input[0] = p.X;        input[1] = p.Y;        input[2] = s;        bool success;        assembly {            success := staticcall(sub(gas, 2000), 7, input, 0x80, r, 0x60)            // Use "invalid" to make gas estimation work            switch success case 0 { invalid() }        }        require (success);    }    /// @return the result of computing the pairing check    /// e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1    /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should    /// return true.    function pairing(G1Point[] p1, G2Point[] p2) view internal returns (bool) {        require(p1.length == p2.length);        uint elements = p1.length;        uint inputSize = elements * 6;        uint[] memory input = new uint[](inputSize);        for (uint i = 0; i < elements; i++)        {            input[i * 6 + 0] = p1[i].X;            input[i * 6 + 1] = p1[i].Y;            input[i * 6 + 2] = p2[i].X[0];            input[i * 6 + 3] = p2[i].X[1];            input[i * 6 + 4] = p2[i].Y[0];            input[i * 6 + 5] = p2[i].Y[1];        }        uint[1] memory out;        bool success;        assembly {            success := staticcall(sub(gas, 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)            // Use "invalid" to make gas estimation work            switch success case 0 { invalid() }        }        require(success);        return out[0] != 0;    }    /// Convenience method for a pairing check for two pairs.    function pairingProd2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) view internal returns (bool) {        G1Point[] memory p1 = new G1Point[](2);        G2Point[] memory p2 = new G2Point[](2);        p1[0] = a1;        p1[1] = b1;        p2[0] = a2;        p2[1] = b2;        return pairing(p1, p2);    }    /// Convenience method for a pairing check for three pairs.    function pairingProd3(            G1Point a1, G2Point a2,            G1Point b1, G2Point b2,            G1Point c1, G2Point c2    ) view internal returns (bool) {        G1Point[] memory p1 = new G1Point[](3);        G2Point[] memory p2 = new G2Point[](3);        p1[0] = a1;        p1[1] = b1;        p1[2] = c1;        p2[0] = a2;        p2[1] = b2;        p2[2] = c2;        return pairing(p1, p2);    }    /// Convenience method for a pairing check for four pairs.    function pairingProd4(            G1Point a1, G2Point a2,            G1Point b1, G2Point b2,            G1Point c1, G2Point c2,            G1Point d1, G2Point d2    ) view internal returns (bool) {        G1Point[] memory p1 = new G1Point[](4);        G2Point[] memory p2 = new G2Point[](4);        p1[0] = a1;        p1[1] = b1;        p1[2] = c1;        p1[3] = d1;        p2[0] = a2;        p2[1] = b2;        p2[2] = c2;        p2[3] = d2;        return pairing(p1, p2);    }}contract Verifier {    using Pairing for *;    struct VerifyingKey {        Pairing.G2Point A;        Pairing.G1Point B;        Pairing.G2Point C;        Pairing.G2Point gamma;        Pairing.G1Point gammaBeta1;        Pairing.G2Point gammaBeta2;        Pairing.G2Point Z;        Pairing.G1Point[] IC;    }    struct Proof {        Pairing.G1Point A;        Pairing.G1Point A_p;        Pairing.G2Point B;        Pairing.G1Point B_p;        Pairing.G1Point C;        Pairing.G1Point C_p;        Pairing.G1Point K;        Pairing.G1Point H;    }    function verifyingKey() pure internal returns (VerifyingKey vk) {        vk.A = Pairing.G2Point([11471555659227993821558309627390598340144479079056433628778102631560356945196,17239341673537703655415242914137247675383548768632132758504283130453992776723], [21840111305325018621811764524080084607281106943083417610062232151753686634389,13067312946253218821063730225751104957278386376461583544396248901695822639709]);        vk.B = Pairing.G1Point(2721879735262596409289531315625821875211187332406308292154995988594906268500,11608983819712298545694042439685471022716966939419357601529919546552292307000);        vk.C = Pairing.G2Point([19656925059139513569729113044871911422430902049677076718711718313620952900563,5576668198704353407832150137561073878345331579977637943174817874706151661393], [7281295213299204051302698314874315382763807448228276777317130979029692931165,7916695789970843503650338800940601383317981493254576033370139751926979927902]);        vk.gamma = Pairing.G2Point([18385334434127063906954531571970107990014968017682766911682537921858279464260,8929675488863538648285654541062334049652723184901164017871711012593339580980], [19432375795531858054188736522402749208417298354515266283889952586861218556876,13161708584257346412896507856831520204094726409907232342829227463532753486821]);        vk.gammaBeta1 = Pairing.G1Point(19309821543521419388092516467492689640912329403760280872194124727606354537632,20353285109717075092024467509541543771816731504732018361823602825239940096506);        vk.gammaBeta2 = Pairing.G2Point([3201520417051381658696464743108694422098403880081178875590449795369617089487,20359564875543220411653113034498547098466912194746756771160720992127018464105], [21510509108235571401228940597112459120626333092208380691682294738065279880961,8755324618659484077861518310650940009846072237432653448950145505233310290697]);        vk.Z = Pairing.G2Point([8835066318839081768498527350557716481288255937020646417565557742609989390821,5953981016178255894550021108862323931501144929290961519398762283427286942730], [3417806769259177697822528101809787162567605994409410699303553780002102855950,5695524357745453823070393157667727062025352362824114638963751112519736534075]);        vk.IC = new Pairing.G1Point[](2);        vk.IC[0] = Pairing.G1Point(17891620820348589195818796761006959184449993997577571636032322491763190514010,12899317037026433033044857347124399643982755957431223448882373114262398444006);        vk.IC[1] = Pairing.G1Point(21849736562685629033103191129599121428571890454117937621414449791469254767436,7965584159993075250688571980216552879225831779231603043091430714075231145743);    }    function verify(uint[] input, Proof proof) view internal returns (uint) {        VerifyingKey memory vk = verifyingKey();        require(input.length + 1 == vk.IC.length);        // Compute the linear combination vk_x        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);        for (uint i = 0; i < input.length; i++)            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));        vk_x = Pairing.addition(vk_x, vk.IC[0]);        if (!Pairing.pairingProd2(proof.A, vk.A, Pairing.negate(proof.A_p), Pairing.P2())) return 1;        if (!Pairing.pairingProd2(vk.B, proof.B, Pairing.negate(proof.B_p), Pairing.P2())) return 2;        if (!Pairing.pairingProd2(proof.C, vk.C, Pairing.negate(proof.C_p), Pairing.P2())) return 3;        if (!Pairing.pairingProd3(            proof.K, vk.gamma,            Pairing.negate(Pairing.addition(vk_x, Pairing.addition(proof.A, proof.C))), vk.gammaBeta2,            Pairing.negate(vk.gammaBeta1), proof.B        )) return 4;        if (!Pairing.pairingProd3(                Pairing.addition(vk_x, proof.A), proof.B,                Pairing.negate(proof.H), vk.Z,                Pairing.negate(proof.C), Pairing.P2()        )) return 5;        return 0;    }    function verifyProof(            uint[2] a,            uint[2] a_p,            uint[2][2] b,            uint[2] b_p,            uint[2] c,            uint[2] c_p,            uint[2] h,            uint[2] k,            uint[1] input        ) view public returns (bool r) {        Proof memory proof;        proof.A = Pairing.G1Point(a[0], a[1]);        proof.A_p = Pairing.G1Point(a_p[0], a_p[1]);        proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);        proof.B_p = Pairing.G1Point(b_p[0], b_p[1]);        proof.C = Pairing.G1Point(c[0], c[1]);        proof.C_p = Pairing.G1Point(c_p[0], c_p[1]);        proof.H = Pairing.G1Point(h[0], h[1]);        proof.K = Pairing.G1Point(k[0], k[1]);        uint[] memory inputValues = new uint[](input.length);        for(uint i = 0; i < input.length; i++){            inputValues[i] = input[i];        }        if (verify(inputValues, proof) == 0) {            return true;        } else {            return false;        }    }}

在这里插入图片描述

在这里插入图片描述
Snarkjs有性能瓶颈,在处理百万级的constraints,需要的内存很大,后续有计划采用Julia重写(Julia 是一个面向科学计算的高性能动态高级程序设计语言,擅长数值计算)。具体参见
在这里插入图片描述

verifier.sol中含verifier和pairing两个智能合约,使用时,注意:

1)不要使用JavaScirpt VM(网页会崩溃),可使用MetaMask+Ropsten测试网络,测试ether可通过测试水管获取。
2)部署时,仅需部署verifier智能合约即可。
3)snarkjs generatecall生成的内容贴入到verifyProof方框后即可,点击call按钮,返回值为true,说明验证通过。

["0x2dfb574f7e62a4d230dddf8d94113b72fcdab705a3db3226f6efdb794e1789d9", "0x248e14f6bce3d13e8957eefea9935eef578fa6013c6563f2d58fd59e352e0886"],["0x085fb4366ea4140449a9f36e1d113f24059a8aff80f54c324fbdb0cf64699975", "0x103b4b855dcc74f419dfd46b3b462eeb8e554fff68ccbbc745152236a91817cb"],[["0x2aebc30dafa7eefae49c21d03687823745bd332f409ee877afca6feeb0e1240e", "0x166de7fa3dad364c96d8d94635c7cb84a2859c1ddcaf16ef0bb30a76c5eb0687"],["0x2305286bb810764dcd0088352ca439ad79b1b00a05f9cc1a16467cb486471e9d", "0x18e66e9dc43142b7cf3a7bc0e2af54baf956fb757d4c03d594abed7b6e735af1"]],["0x0e7a5775de7bdf7258fce0eb7f12422cbfd02f7ce30de00061847969d9c808a4", "0x02103f55b366e862fa07b18ed1c2731c4e909b642295902bd81d360d42fccc3f"],["0x25b2c759605d59687a7171dfa895cb5e7486079b4a2ebae9267a0eb2544968da", "0x277790ef0262b98997a0fa60cc79ab2ce28a777bafb22d213d0d5706953c4c86"],["0x069fe35474bfa91b4e1edab772a7ae66dae6fc7b77e283462ccc32b54b5b2f9e", "0x06fdf5b40ad74ccbcbe0ee6aca7c702f86631ee900f9ce753a47eb21daaa329a"],["0x17d9388be73e9c093c40e789099c262881b0df6e759d0207c25ebbd2263b260f", "0x15e1b6a737c9f2001527040fbba111fd1b9606bca3fc58d3421ec1a95891d269"],["0x0a7e62670f7a8a5ee4f5d501df479cc951053f0f4209a7a1f2b7b263fd447744", "0x0e7f923878de5f6c4cdf3ad21b37e9f1dea419aea1e0edc30991f3c00b0ac2e5"],["0x0000000000000000000000000000000000000000000000000000000000000021"]

4)初次配置MetaMask时,若Remix选择Injected Web3无法切换,重启浏览器即可生效。

在这里插入图片描述

在这里插入图片描述

转载地址:http://puqx.baihongyu.com/

你可能感兴趣的文章
MySQL5.1安装
查看>>
mysql5.5和5.6版本间的坑
查看>>
mysql5.5最简安装教程
查看>>
mysql5.6 TIME,DATETIME,TIMESTAMP
查看>>
mysql5.6.21重置数据库的root密码
查看>>
Mysql5.6主从复制-基于binlog
查看>>
MySQL5.6忘记root密码(win平台)
查看>>
MySQL5.6的Linux安装shell脚本之二进制安装(一)
查看>>
MySQL5.6的zip包安装教程
查看>>
mysql5.7 for windows_MySQL 5.7 for Windows 解压缩版配置安装
查看>>
Webpack 基本环境搭建
查看>>
mysql5.7 安装版 表不能输入汉字解决方案
查看>>
MySQL5.7.18主从复制搭建(一主一从)
查看>>
MySQL5.7.19-win64安装启动
查看>>
mysql5.7.19安装图解_mysql5.7.19 winx64解压缩版安装配置教程
查看>>
MySQL5.7.37windows解压版的安装使用
查看>>
mysql5.7免费下载地址
查看>>
mysql5.7命令总结
查看>>
mysql5.7安装
查看>>
mysql5.7性能调优my.ini
查看>>