0. 实验环境
- Ubuntu Linux 20.04
- Node.js v18.12.1
- [email protected]
- [email protected]
apt安装的nodejs版本一直报错,需要安装更高版本的nodejs,直接从官网下载nodejs再配置bin目录的环境变量即可。
将下载的node-v18.12.1-linux-x64安装包解压到/user/local目录下。
然后配置环境变量,在/etc/profile文件末尾添加如下内容:
export PATH=/usr/local/node-v18.12.1-linux-x64/bin:$PATH
- 1
然后执行刷新环境变量的命令:
source /etc/profile
- 1
npm官方内容被屏蔽了,需要设置npm的国内源:
npm config set registry http://mirrors.cloud.tencent.com/npm/
- 1
安装circom和snarkjs:
npm install -g snarkjs
npm install -g circom
- 1
- 2
这里npm直接安装的circom版本是0.5.46,不过现在基本上通用的都是circom 2.0了,circom 2.0的安装教程来自官网:https://docs.circom.io/getting-started/installation/
1. 理解zkSNARK
很多博客与综述都在讲解zkSNARK的实现原理,而本篇文章主要从应用的角度阐述zkSNARK。估计读者都是计算机安全领域的探索者,已经有zkSNARK的理论基础,我简略地写下这篇文章是想展示一下zkSNARK的具体实现流程,让学者们在理论研究之余尝试一下调用库实现zkSNARK的步骤,加深对zkSNARK的理解。
在本示例中,首先选取两个大素数p和q,向外界公开两数相乘的结果n=p*q,使用zkSNARK向验证者证明自己知道整数n的两个素因子,但是不想透漏p和q的具体数值,实现“零知识”证明。
2. 编写电路circuit.circom并编译
zkSNARK需要将待证明的程序运算关系转化成多项式方程,向外界证明等式成立。其中,多项式的运算过程以电路的形式表示,证明自己的私有输入与公开参数满足电路关系,但是不泄露私有输入的具体内容。
新建文件命名为circuit.circom,输入如下内容:
template circuit() {
signal private input a;
signal private input b;
signal output c;
c <== a*b;
}
component main = circuit();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
circom的语法在官网有详细解释。
signal理解为电路中的连线,不能随意赋值。与一般编程语言的变量在内存中任意赋值不同。
这一段代码的意思是,私有输入a和b的乘积必须与c相等,称为“约束条件”。
执行编译命令:
circom circuit.circom --r1cs --wasm --sym
- 1
生成三个文件circuit.r1cs、circuit.wasm、circuit.sym。
查看约束电路的信息:
snarkjs ri circuit.r1cs
[INFO] snarkJS: Curve: bn-128
[INFO] snarkJS: # of Wires: 4
[INFO] snarkJS: # of Constraints: 1
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Labels: 4
[INFO] snarkJS: # of Outputs: 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
snarkjs rp circuit.r1cs
[INFO] snarkJS: [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.a ] * [ main.b ] - [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.c ] = 0
- 1
- 2
- 3
3. 根据私有输入input.json生成witness
新建文件input.json,编写私有输入:
{"a": 3, "b": 7}
- 1
这里的a和b就是电路中的两个私有输入。
生成witness:
snarkjs wc circuit.wasm input.json witness.wtns
- 1
将witness转化成json格式:
snarkjs wej witness.wtns witness.json
[
"1",
"21",
"3",
"7"
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这里的数组就是r1cs里面的向量。
4. 可信设置
利用powersOfTau进行可信设置:
snarkjs ptn bn128 10 powersOfTau10_0000.ptau
snarkjs pt2 powersOfTau10_0000.ptau pot10_final.ptau
- 1
- 2
- 3
5. 生成验证密钥
执行如下命令:
snarkjs zkn circuit.r1cs pot10_final.ptau circuit_0000.zkey
snarkjs zkev circuit_0000.zkey
- 1
- 2
- 3
生成了验证密钥文件verification_key.json:
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 1,
"vk_alpha_1": [
"1",
"2",
"1"
],
"vk_beta_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_delta_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"17264119758069723980713015158403419364912226240334615592005620718956030922389",
"1300711225518851207585954685848229181392358478699795190245709208408267917898"
],
[
"8894217292938489450175280157304813535227569267786222825147475294561798790624",
"1829859855596098509359522796979920150769875799037311140071969971193843357227"
],
[
"4968700049505451466697923764727215585075098085662966862137174841375779106779",
"12814315002058128940449527172080950701976819591738376253772993495204862218736"
]
],
[
[
"4233474252585134102088637248223601499779641130562251948384759786370563844606",
"9420544134055737381096389798327244442442230840902787283326002357297404128074"
],
[
"13457906610892676317612909831857663099224588803620954529514857102808143524905",
"5122435115068592725432309312491733755581898052459744089947319066829791570839"
],
[
"8891987925005301465158626530377582234132838601606565363865129986128301774627",
"440796048150724096437130979851431985500142692666486515369083499585648077975"
]
]
],
"IC": [
[
"0",
"1",
"0"
],
[
"1",
"21888242871839275222246405745257275088696311157297823662689037894645226208581",
"1"
]
]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
6. 生成proof
执行生成proof的命令:
snarkjs g16p circuit_0000.zkey witness.wtns
- 1
生成了公共参数public.json:
[
"21"
]
- 1
- 2
- 3
生成了证明proof.json:
{
"pi_a": [
"19989501194150232030287836231786249461121144567445024823064735350287474506223",
"21655946435202999151519101772821474971537905530411470752874989158703798792312",
"1"
],
"pi_b": [
[
"16849405748801758296101495735786887364124830646811289624179679686910073081847",
"8262572077976115071765367472495699796017916450544371038971924549367335179729"
],
[
"12958773022321817503613723687947680235067294837906750271427696212863641553842",
"7087347573327104818719324688424308951919471849681614073561604633419212094"
],
[
"1",
"0"
]
],
"pi_c": [
"9246929219549012289734255058845496297360333953855121701793792902617376293680",
"21873404623428113396140883503409567642025944127192624541920936238800869635767",
"1"
],
"protocol": "groth16",
"curve": "bn128"
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
7. 验证proof
执行验证proof的命令:
snarkjs g16v verification_key.json public.json proof.json
[INFO] snarkJS: OK!
- 1
- 2
- 3
8. 导出solidity智能合约源码
执行命令生成验证proof的智能合约solidity源码:
snarkjs zkey export solidityverifier circuit_0000.zkey verifier.sol
- 1
//
// 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.
//
// 2019 OKIMS
// ported to solidity 0.6
// fixed linter warnings
// added requiere error messages
//
//
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.11;
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() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
/// @return the generator of G2
function P2() internal pure returns (G2Point memory) {
// Original code point
return G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
/*
// Changed by Jordi point
return G2Point(
[10857046999023057135944570762232829481370756359578518086990519993285655852781,
11559732032986387107991004021392285783925812861821192530917403151452391805634],
[8495653923123431417604973247489272438418190587263600148770280649306958101930,
4082367875863433681332203403145435568316851327593401208105741076214120093531]
);
*/
}
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
// 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 r the sum of two points of G1
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
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,"pairing-add-failed");
}
/// @return r 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 memory p, uint s) internal view returns (G1Point memory r) {
uint[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
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,"pairing-mul-failed");
}
/// @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[] memory p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length,"pairing-lengths-failed");
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;
// solium-disable-next-line security/no-inline-assembly
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,"pairing-opcode-failed");
return out[0] != 0;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view 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 memory a1, G2Point memory a2,
G1Point memory b1, G2Point memory b2,
G1Point memory c1, G2Point memory c2
) internal view 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 memory a1, G2Point memory a2,
G1Point memory b1, G2Point memory b2,
G1Point memory c1, G2Point memory c2,
G1Point memory d1, G2Point memory d2
) internal view 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.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
1,
2
);
vk.beta2 = Pairing.G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
vk.gamma2 = Pairing.G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
vk.delta2 = Pairing.G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
vk.IC = new Pairing.G1Point[](2);
vk.IC[0] = Pairing.G1Point(
0,
1
);
vk.IC[1] = Pairing.G1Point(
1,
21888242871839275222246405745257275088696311157297823662689037894645226208581
);
}
function verify(uint[] memory input, Proof memory proof) internal view returns (uint) {
uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length,"verifier-bad-input");
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
for (uint i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field,"verifier-gte-snark-scalar-field");
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.pairingProd4(
Pairing.negate(proof.A), proof.B,
vk.alfa1, vk.beta2,
vk_x, vk.gamma2,
proof.C, vk.delta2
)) return 1;
return 0;
}
/// @return r bool true if proof is valid
function verifyProof(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[1] memory input
) public view returns (bool r) {
Proof memory proof;
proof.A = Pairing.G1Point(a[0], a[1]);
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
proof.C = Pairing.G1Point(c[0], c[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;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
9. 导出智能合约调用参数
执行以下命令:
snarkjs zkesc public.json proof.json
- 1
得到智能合约参数列表:
["0x2c31a81fc8fffb039b2bddc37bb14a140ab018433fd53c9466d00fe36bc639ef", "0x2fe0d4d12a03f74cc73ef9f647c4885f497d4f9dce0ff3dc9328f4a1add1b078"],[["0x12447337b8a54079c9abca925b273afddbd111adafeb0cfebaf454f5105f55d1", "0x25406cfa638aad3c35fbd02b9ffebf2251499ed5259c1f08948a7410ab4f8ff7"],["0x000402e4550530aa896b4969fc8b10caa7bde28d1a5507b36091a9af676e653e", "0x1ca667d5ff619b39d02f1ab56a3ed332cbd95aa691b1d4b02bab50d206958bb2"]],["0x147193935c7932b1bf375bfb0be81fee00713cf2660495361eeee7d514150330", "0x305be885a82d6047035324468dff233b3e0afa14bb16ecca04967f8fd645b6b7"],["0x0000000000000000000000000000000000000000000000000000000000000015"]
- 1
可能是生成的智能合约中涉及到不可用的opcode,remix和ganache都没有执行成功,后续我再测试一下geth等以太坊平台。
目录
干货分享,感谢您的阅读!
一、IPv4地址在数据库中的存储方式?
在Java开发过程中,我们通常会使用数据库来存储各种类型的数据,包括IPv4地址。但是,我们应该如何存储这些IP地址才能最大程度地提高系统的性能呢?使用字符串存储IPv4地址可能会更直观,但是否是最佳选择呢?
究竟应该如何存储IPv4地址才能最大程度地提高数据库的性能和系统的效率呢?我们进行探讨下为何数据库推荐将IPv4地址存储为32位整数而非字符串,并从Java开发者的角度分析这种存储方式对系统性能和开发流程的影响。
二、IPv4地址的存储方式比较
(一)字符串存储 vs 整数存储
class="table-box">存储方式 | 优点 | 缺点 |
---|---|---|
字符串存储 | 直观性:更易于人类理解和查看 灵活性:可以存储不同格式的IPv4地址 | 存储空间消耗大:每个IPv4地址需要较大的存储空间 效率低下:字符串比较通常耗时较长 难以进行数学运算和比较:需要额外的格式转换和解析 |
整数存储 | 存储空间效率高:只需较小存储空间 查询效率高:整数比较速度更快,索引和查询效率更高 数学运算方便:可以直接进行数学运算和比较 | 可能丢失部分信息:无法存储IPv6地址等其他地址格式 不直观:整数表示的IPv4地址不易于人类理解和查看 |
评论记录:
回复评论: