当谈及零知识电路开拓时,我们有很多可选的库。实现我创造iden3开拓的circom和snarkjs 对付零知识证明的新手很友好:电路很随意马虎编写,全体零知识证明框架 的运行也很大略,不须要繁芜的环境。
建议你在学习本教程之前先阅读circom/snarkjs的官方教程。 本教程的完全代码可以从这里下载。
用自己熟习的措辞学习以太坊DApp开拓: Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、零知识扑克牌游戏规则简介
为了突出重点,我们简化了扑克牌游戏的规则:
每个选手发5张牌不可以换牌当比较牌大小时,只考虑对子(pairs),不考虑顺子(straights)、同花(flushes)、满堂红(full houses)等等情形选手可选的动作只有:不跟(fold)、下注(see)、加注(raise)如果一手牌里没有对子,选手就不能叫牌(bid)还有末了一个规则 —— 不许可矫揉造作(bluffing) —— 这便是我们要用零知识证据来担保的。 叫牌的选手可以避免透露自己的牌,但同时依然可以向其他选手证明自己的确有对子,不是 矫揉造作。不才面的教程中,我们将忽略游戏机制而聚焦于零知识证明电路的设计与实现。
2、零知识扑克牌游戏的电路概述我们这个基于零知识证明的扑克牌游戏的电路,大致该当是这样的:
采集输入:牌型、选手的叫牌选择确认牌面中至少有一个对子评估叫牌类型,例如跟进或弃牌设置一个约束来检讨是否已经进行选择设置一个约束来检讨所选择的选项符合当前的牌型下面就让我们分步骤学习这个电路的实当代码。
3、引入circom供应的根本电路首先我们须要引入一些circom供应的根本电路:
include “../node_modules/circomlib/circuits/gates.circom”;include “../node_modules/circomlib/circuits/comparators.circom”;
须要指出的是,我们的文件夹构造看起来是这样:
project-root\poker
因此我们利用上面的路径来引入根本电路。这些根本电路时circomlib 供应的,因此须要进行额外的安装:
npm install --save circomlib
4、定义扑克牌游戏的零知识电路模板
template Poker() { ... circuit body ...}component main = Poker();
这个构造基本和官方教程里一样,只有一个模板并且定义了入口点。
5、定义扑克牌游戏的输入、输出和中间结果signal private input cards[5]; // Each 2..14signal input isSee; // 1 or 0signal input isFold; // 1 or 0signal input raise; // intsignal output out; // 1 or 0// Intermediate resultssignal isBid;signal isRaise;signal hasChosen;
牌型信息定义为一个数组:cards[5],每一张牌都用其牌面值 表示,我们忽略花色。例如 Ace=14、King=13、Queen=12、Jack=11… 依次类推直至2。选手的牌型是电路的私有输入,这表示我们哀求 这个输入保持隐秘。
叫牌选项当然是公开的,由于该信息须要向所有其他选手公布。 弃牌或者跟随下注选项都用布尔值表示,加注选项则是整数 类型,表示实际增加的注数。
唯一的输出是out,值为1或0。
我们还定义了其他一些中间值,稍后在进行先容。
6、检讨牌型中的对子数量这一部分的代码用于决定牌型中是否包含对子。把稳这部分代码看起来 和普通javascript非常像,不包含任何约束定义或者旗子暗记操作。
这里是关于电路事情的一个直觉。现实生活中的电路可能须要一些预处理 或者中间评估,但是把稳,这样的代码不会包含于正式的证据中,只有那些 约束会嵌入零知识证明公共参数中,并在证据天生和验证节点 。一个不老实 的证明人可能会修正中间结果来假造见证。
预处理环节的结果是一个单独的变量numPairs,在后面部分会用到。
这里的实现逻辑便是两重循环进行配对并打算对子的总数:
// Count pairsvar numPairs = 0;for (var i=0; i<4; i++) { for (var j=i+1; j<5; j++) { if (cards[i] == cards[j]) { numPairs++; // break doesn’t work. Just force j and i to exit j = 5; i = 5; } }}
7、零知识扑克牌游戏中的电路约束
在之前的入门教程中我们先容了电路旗子暗记的操作符号: <–, –>, <==, ==> , 和 === 。 这个教程中我们引入新的符号:组件。组件便是类的实例,把稳我们调用组件的方法, 组件的输入旗子暗记(a、b、c、in等等)被赋值并利用旗子暗记操作符。组件的数据旗子暗记也可以 类似方法利用。
在这里我们利用组件进行布尔运算,circomlib供应了不少电路组件可供我们利用。
同样,我们定义了约束,个中一个约束是检讨选手是否做出了选择(fold、see或raise)。 另一个要检讨的约束便是看是否存在对子。终极输出旗子暗记的值为1。
// isRaise = (raise != 0)isRaise <-- (raise > 0);isBid <-- (isRaise || isSee); // Constraint: Must be either bid or fold: isBid XOR isFold = 1hasChosen <-- isBid + isFold — 2isBidisFold;hasChosen === 1; // Constraint: numPairs must be > 0 if isBid = 1var hasPairs = (numPairs > 0);component not3 = NOT();not3.in <-- isBid; component or2 = OR();or2.a <-- hasPairs;or2.b <-- not3.out;or2.out === 1; out <-- or2.out;
8、零知识电路的公共参数设置、证据天生和验证
首先利用电路进行设置,天生公共参数:
circom poker.circom -o poker.jsonsnarkjs setup -o poker.json
接下来须要供应输入,例如下面这个大略的输入文件input.json:
{“cards”: [8, 7, 4, 7, 13], “isFold”: 0, “isSee”: 0, “raise”: 10 }
这手牌里包含一个对子,因此所有的叫牌选项都可用。选手选择了加注10。 我们预期这个输入是有效的。输入信息中只有cards是私有的,而其他西悉尼都是 公开的。
现在来天生证据:
snarkjs calculatewitness -c poker.jsonsnarkjs proof
证据天生的性能繁芜度为O(n),个中n表示电路中约束的个数。由于 我们的电路很大略,因此约束很少,证据天生也快。如果你的电路中有 非常多的约束就须要更多的韶光天生证据,你可以查看这个测试 来理解电路性能与约束数量的关系。
现在用验证器来验证证据:
PS C:\dev\snarks\poker> snarkjs verifyOK
的确和预期一样!
原文链接:http://blog.hubwiz.com/2020/05/31/zkpoker-tutorial/