博客
关于我
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中cast() 和convert()的用法讲解
查看>>
mysql中datetime与timestamp类型有什么区别
查看>>
MySQL中DQL语言的执行顺序
查看>>
mysql中floor函数的作用是什么?
查看>>
MySQL中group by 与 order by 一起使用排序问题
查看>>
mysql中having的用法
查看>>
MySQL中interactive_timeout和wait_timeout的区别
查看>>
mysql中int、bigint、smallint 和 tinyint的区别、char和varchar的区别详细介绍
查看>>
mysql中json_extract的使用方法
查看>>
mysql中json_extract的使用方法
查看>>
mysql中kill掉所有锁表的进程
查看>>
mysql中like % %模糊查询
查看>>
MySql中mvcc学习记录
查看>>
mysql中null和空字符串的区别与问题!
查看>>
MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入
查看>>
MYSQL中TINYINT的取值范围
查看>>
MySQL中UPDATE语句的神奇技巧,让你操作数据库如虎添翼!
查看>>
Mysql中varchar类型数字排序不对踩坑记录
查看>>
MySQL中一条SQL语句到底是如何执行的呢?
查看>>
MySQL中你必须知道的10件事,1.5万字!
查看>>