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

你可能感兴趣的文章
Netty 异步任务调度与异步线程池
查看>>
Netty 的 Handler 链调用机制
查看>>
Netty 编解码器详解
查看>>
Netty 解决TCP粘包/半包使用
查看>>
Netty 调用,效率这么低还用啥?
查看>>
Netty 高性能架构设计
查看>>
Netty+Protostuff实现单机压测秒级接收35万个对象实践经验分享
查看>>
Netty+SpringBoot+FastDFS+Html5实现聊天App详解(一)
查看>>
netty--helloword程序
查看>>
netty2---服务端和客户端
查看>>
Netty5.x 和3.x、4.x的区别及注意事项(官方翻译)
查看>>
netty——bytebuf的创建、内存分配与池化、组成、扩容规则、写入读取、内存回收、零拷贝
查看>>
netty——Channl的常用方法、ChannelFuture、CloseFuture
查看>>
netty——EventLoop概念、处理普通任务定时任务、处理io事件、EventLoopGroup
查看>>
netty——Future和Promise的使用 线程间的通信
查看>>
netty——Handler和pipeline
查看>>
Vue输出HTML
查看>>
netty——黏包半包的解决方案、滑动窗口的概念
查看>>
Netty中Http客户端、服务端的编解码器
查看>>
Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息
查看>>