博客
关于我
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/

你可能感兴趣的文章
MYSQL输入密码后闪退的解决方法
查看>>
MySQL迁移到达梦:如何轻松、高质量完成迁移任务
查看>>
mysql返回的时间和实际数据存储的时间有误差(java+mysql)
查看>>
mysql还有哪些自带的函数呢?别到处找了,看这个就够了。
查看>>
Mysql进入数据库
查看>>
mysql进阶 with-as 性能调优
查看>>
mysql进阶-查询优化-慢查询日志
查看>>
wargame narnia writeup
查看>>
MySQL进阶篇SQL优化(InnoDB锁问题排查与解决)
查看>>
Mysql进阶索引篇03——2个新特性,11+7条设计原则教你创建索引
查看>>
mysql远程连接设置
查看>>
MySql连接出现1251Client does not support authentication protocol requested by server解决方法
查看>>
Mysql连接时报时区错误
查看>>
MySql连接时提示:unknown Mysql server host
查看>>
MySQL连环炮,你扛得住嘛?
查看>>
mysql逗号分隔的字符串如何搜索
查看>>
MySQL通用优化手册
查看>>
Mysql通过data文件恢复
查看>>
MYSQL遇到Deadlock found when trying to get lock,解决方案
查看>>
MYSQL遇到Deadlock found when trying to get lock,解决方案
查看>>