@semaphore-protocol/proof 4.0.0-beta.4 → 4.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.js +96 -160
- package/dist/index.node.cjs +114 -199
- package/dist/index.node.js +112 -197
- package/dist/types/generate-proof.d.ts +1 -1
- package/package.json +4 -10
- package/src/generate-proof.ts +1 -1
package/dist/index.browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @semaphore-protocol/proof
|
|
3
|
-
* @version 4.0.0-beta.
|
|
3
|
+
* @version 4.0.0-beta.6
|
|
4
4
|
* @file A library to generate and verify Semaphore proofs.
|
|
5
5
|
* @copyright Ethereum Foundation 2024
|
|
6
6
|
* @license MIT
|
|
@@ -15,73 +15,19 @@ import { keccak256 } from 'ethers/crypto';
|
|
|
15
15
|
import { toBeHex, toBigInt as toBigInt$1 } from 'ethers/utils';
|
|
16
16
|
import { encodeBytes32String } from 'ethers/abi';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
__assign = Object.assign || function __assign(t) {
|
|
20
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
21
|
-
s = arguments[i];
|
|
22
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
23
|
-
}
|
|
24
|
-
return t;
|
|
25
|
-
};
|
|
26
|
-
return __assign.apply(this, arguments);
|
|
27
|
-
};
|
|
28
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
29
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function __generator(thisArg, body) {
|
|
38
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
39
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
40
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
41
|
-
function step(op) {
|
|
42
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
43
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
44
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
45
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
46
|
-
switch (op[0]) {
|
|
47
|
-
case 0: case 1: t = op; break;
|
|
48
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
49
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
50
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
51
|
-
default:
|
|
52
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
53
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
54
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
55
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
56
|
-
if (t[2]) _.ops.pop();
|
|
57
|
-
_.trys.pop(); continue;
|
|
58
|
-
}
|
|
59
|
-
op = body.call(thisArg, _);
|
|
60
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
61
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
65
|
-
var e = new Error(message);
|
|
66
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
67
|
-
};
|
|
68
|
-
|
|
18
|
+
/* istanbul ignore file */
|
|
69
19
|
/**
|
|
70
20
|
* Returns the zero-knowledge artifact paths. Artifacts exist for each tree
|
|
71
21
|
* depth supported by Semaphore, and they were generated in a trusted-setup.
|
|
72
22
|
* @param treeDepth The depth of the tree.
|
|
73
23
|
* @returns The zero-knowledge artifacts paths.
|
|
74
24
|
*/
|
|
75
|
-
function getSnarkArtifacts(treeDepth) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
zkeyFilePath: "https://semaphore.cedoor.dev/artifacts/".concat(treeDepth, "/semaphore.zkey")
|
|
82
|
-
}];
|
|
83
|
-
});
|
|
84
|
-
});
|
|
25
|
+
async function getSnarkArtifacts(treeDepth) {
|
|
26
|
+
requireNumber(treeDepth, "treeDepth");
|
|
27
|
+
return {
|
|
28
|
+
wasmFilePath: `https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.wasm`,
|
|
29
|
+
zkeyFilePath: `https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.zkey`
|
|
30
|
+
};
|
|
85
31
|
}
|
|
86
32
|
|
|
87
33
|
/**
|
|
@@ -124,89 +70,80 @@ function toBigInt(value) {
|
|
|
124
70
|
* or they will be automatically fetched.
|
|
125
71
|
* @param identity The Semaphore identity.
|
|
126
72
|
* @param groupOrMerkleProof The Semaphore group or its Merkle proof.
|
|
127
|
-
* @param scope The Semaphore scope.
|
|
128
73
|
* @param message The Semaphore message.
|
|
74
|
+
* @param scope The Semaphore scope.
|
|
129
75
|
* @param merkleTreeDepth The depth of the tree with which the circuit was compiled.
|
|
130
76
|
* @param snarkArtifacts The SNARK artifacts.
|
|
131
77
|
* @returns The Semaphore proof ready to be verified.
|
|
132
78
|
*/
|
|
133
|
-
function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
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
|
-
merkleTreeRoot: publicSignals[0],
|
|
202
|
-
nullifier: publicSignals[1],
|
|
203
|
-
message: message.toString(),
|
|
204
|
-
scope: scope.toString(),
|
|
205
|
-
points: packGroth16Proof(proof)
|
|
206
|
-
}];
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
});
|
|
79
|
+
async function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
80
|
+
requireDefined(identity, "identity");
|
|
81
|
+
requireDefined(groupOrMerkleProof, "groupOrMerkleProof");
|
|
82
|
+
requireDefined(message, "message");
|
|
83
|
+
requireDefined(scope, "scope");
|
|
84
|
+
requireObject(identity, "identity");
|
|
85
|
+
requireObject(groupOrMerkleProof, "groupOrMerkleProof");
|
|
86
|
+
requireTypes(message, "message", ["string", "bigint", "number", "Uint8Array"]);
|
|
87
|
+
requireTypes(scope, "scope", ["string", "bigint", "number", "Uint8Array"]);
|
|
88
|
+
if (merkleTreeDepth) {
|
|
89
|
+
requireNumber(merkleTreeDepth, "merkleTreeDepth");
|
|
90
|
+
}
|
|
91
|
+
if (snarkArtifacts) {
|
|
92
|
+
requireObject(snarkArtifacts, "snarkArtifacts");
|
|
93
|
+
}
|
|
94
|
+
// Message and scope can be strings, numbers or buffers (i.e. Uint8Array).
|
|
95
|
+
// They will be converted to bigints anyway.
|
|
96
|
+
message = toBigInt(message);
|
|
97
|
+
scope = toBigInt(scope);
|
|
98
|
+
let merkleProof;
|
|
99
|
+
// The second parameter can be either a Merkle proof or a group.
|
|
100
|
+
// If it is a group the Merkle proof will be calculated here.
|
|
101
|
+
if ("siblings" in groupOrMerkleProof) {
|
|
102
|
+
merkleProof = groupOrMerkleProof;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const leafIndex = groupOrMerkleProof.indexOf(identity.commitment);
|
|
106
|
+
merkleProof = groupOrMerkleProof.generateMerkleProof(leafIndex);
|
|
107
|
+
}
|
|
108
|
+
const merkleProofLength = merkleProof.siblings.length;
|
|
109
|
+
if (merkleTreeDepth !== undefined) {
|
|
110
|
+
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
|
|
111
|
+
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
merkleTreeDepth = merkleProofLength;
|
|
116
|
+
}
|
|
117
|
+
// If the Snark artifacts are not defined they will be automatically downloaded.
|
|
118
|
+
if (!snarkArtifacts) {
|
|
119
|
+
snarkArtifacts = await getSnarkArtifacts(merkleTreeDepth);
|
|
120
|
+
}
|
|
121
|
+
// The index must be converted to a list of indices, 1 for each tree level.
|
|
122
|
+
// The missing siblings can be set to 0, as they won't be used in the circuit.
|
|
123
|
+
const merkleProofIndices = [];
|
|
124
|
+
const merkleProofSiblings = merkleProof.siblings;
|
|
125
|
+
for (let i = 0; i < merkleTreeDepth; i += 1) {
|
|
126
|
+
merkleProofIndices.push((merkleProof.index >> i) & 1);
|
|
127
|
+
if (merkleProofSiblings[i] === undefined) {
|
|
128
|
+
merkleProofSiblings[i] = "0";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const { proof, publicSignals } = await groth16.fullProve({
|
|
132
|
+
secret: identity.secretScalar,
|
|
133
|
+
merkleProofLength,
|
|
134
|
+
merkleProofIndices,
|
|
135
|
+
merkleProofSiblings,
|
|
136
|
+
scope: hash(scope),
|
|
137
|
+
message: hash(message)
|
|
138
|
+
}, snarkArtifacts.wasmFilePath, snarkArtifacts.zkeyFilePath);
|
|
139
|
+
return {
|
|
140
|
+
merkleTreeDepth,
|
|
141
|
+
merkleTreeRoot: publicSignals[0],
|
|
142
|
+
nullifier: publicSignals[1],
|
|
143
|
+
message: message.toString(),
|
|
144
|
+
scope: scope.toString(),
|
|
145
|
+
points: packGroth16Proof(proof)
|
|
146
|
+
};
|
|
210
147
|
}
|
|
211
148
|
|
|
212
149
|
var protocol = "groth16";
|
|
@@ -789,26 +726,25 @@ var verificationKeys = {
|
|
|
789
726
|
* @param proof The Semaphore proof.
|
|
790
727
|
* @returns True if the proof is valid, false otherwise.
|
|
791
728
|
*/
|
|
792
|
-
function verifyProof(proof) {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
});
|
|
729
|
+
async function verifyProof(proof) {
|
|
730
|
+
requireDefined(proof, "proof");
|
|
731
|
+
requireObject(proof, "proof");
|
|
732
|
+
const { merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points } = proof;
|
|
733
|
+
requireNumber(merkleTreeDepth, "proof.merkleTreeDepth");
|
|
734
|
+
requireString(merkleTreeRoot, "proof.merkleTreeRoot");
|
|
735
|
+
requireString(nullifier, "proof.nullifier");
|
|
736
|
+
requireString(message, "proof.message");
|
|
737
|
+
requireString(scope, "proof.scope");
|
|
738
|
+
requireArray(points, "proof.points");
|
|
739
|
+
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
|
|
740
|
+
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`);
|
|
741
|
+
}
|
|
742
|
+
const verificationKey = {
|
|
743
|
+
...verificationKeys,
|
|
744
|
+
vk_delta_2: verificationKeys.vk_delta_2[merkleTreeDepth - 1],
|
|
745
|
+
IC: verificationKeys.IC[merkleTreeDepth - 1]
|
|
746
|
+
};
|
|
747
|
+
return groth16.verify(verificationKey, [merkleTreeRoot, nullifier, hash(message), hash(scope)], unpackGroth16Proof(points));
|
|
812
748
|
}
|
|
813
749
|
|
|
814
750
|
export { generateProof, verifyProof };
|
package/dist/index.node.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @semaphore-protocol/proof
|
|
3
|
-
* @version 4.0.0-beta.
|
|
3
|
+
* @version 4.0.0-beta.6
|
|
4
4
|
* @file A library to generate and verify Semaphore proofs.
|
|
5
5
|
* @copyright Ethereum Foundation 2024
|
|
6
6
|
* @license MIT
|
|
@@ -13,94 +13,31 @@ var constants = require('@semaphore-protocol/utils/constants');
|
|
|
13
13
|
var errorHandlers = require('@zk-kit/utils/error-handlers');
|
|
14
14
|
var snarkjs = require('snarkjs');
|
|
15
15
|
var node_fs = require('node:fs');
|
|
16
|
-
var promises
|
|
16
|
+
var promises = require('node:fs/promises');
|
|
17
17
|
var os = require('node:os');
|
|
18
18
|
var node_path = require('node:path');
|
|
19
19
|
var node_stream = require('node:stream');
|
|
20
|
-
var promises = require('node:stream/promises');
|
|
20
|
+
var promises$1 = require('node:stream/promises');
|
|
21
21
|
var crypto = require('ethers/crypto');
|
|
22
22
|
var utils = require('ethers/utils');
|
|
23
23
|
var abi = require('ethers/abi');
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
__assign = Object.assign || function __assign(t) {
|
|
27
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
28
|
-
s = arguments[i];
|
|
29
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
30
|
-
}
|
|
31
|
-
return t;
|
|
32
|
-
};
|
|
33
|
-
return __assign.apply(this, arguments);
|
|
34
|
-
};
|
|
35
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
36
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
function __generator(thisArg, body) {
|
|
45
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
46
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
-
function step(op) {
|
|
49
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
-
switch (op[0]) {
|
|
54
|
-
case 0: case 1: t = op; break;
|
|
55
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
-
default:
|
|
59
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
-
if (t[2]) _.ops.pop();
|
|
64
|
-
_.trys.pop(); continue;
|
|
65
|
-
}
|
|
66
|
-
op = body.call(thisArg, _);
|
|
67
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
72
|
-
var e = new Error(message);
|
|
73
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
74
|
-
};
|
|
75
|
-
|
|
25
|
+
/* istanbul ignore file */
|
|
76
26
|
/**
|
|
77
27
|
* A utility function to download the zero-knowledge artifacts from an external server.
|
|
78
28
|
* @param url The URL from which to download the artifacts.
|
|
79
29
|
* @param outputPath The path in which to save the artifacts.
|
|
80
30
|
*/
|
|
81
|
-
function download(url, outputPath) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
dir = node_path.dirname(outputPath);
|
|
93
|
-
return [4 /*yield*/, promises$1.mkdir(dir, { recursive: true })];
|
|
94
|
-
case 2:
|
|
95
|
-
_a.sent();
|
|
96
|
-
fileStream = node_fs.createWriteStream(outputPath);
|
|
97
|
-
return [4 /*yield*/, promises.finished(node_stream.Readable.fromWeb(response.body).pipe(fileStream))];
|
|
98
|
-
case 3:
|
|
99
|
-
_a.sent();
|
|
100
|
-
return [2 /*return*/];
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
});
|
|
31
|
+
async function download(url, outputPath) {
|
|
32
|
+
const response = await fetch(url);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
// Ensure the directory exists.
|
|
37
|
+
const dir = node_path.dirname(outputPath);
|
|
38
|
+
await promises.mkdir(dir, { recursive: true });
|
|
39
|
+
const fileStream = node_fs.createWriteStream(outputPath);
|
|
40
|
+
await promises$1.finished(node_stream.Readable.fromWeb(response.body).pipe(fileStream));
|
|
104
41
|
}
|
|
105
42
|
/**
|
|
106
43
|
* Downloads the zero-knowledge artifacts and returns their paths.
|
|
@@ -109,30 +46,18 @@ function download(url, outputPath) {
|
|
|
109
46
|
* @param treeDepth The depth of the tree.
|
|
110
47
|
* @returns The zero-knowledge artifacts paths.
|
|
111
48
|
*/
|
|
112
|
-
function getSnarkArtifacts(treeDepth) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
_a.sent();
|
|
125
|
-
return [4 /*yield*/, download("https://semaphore.cedoor.dev/artifacts/".concat(treeDepth, "/semaphore.zkey"), "".concat(tmpPath, "/semaphore.zkey"))];
|
|
126
|
-
case 2:
|
|
127
|
-
_a.sent();
|
|
128
|
-
_a.label = 3;
|
|
129
|
-
case 3: return [2 /*return*/, {
|
|
130
|
-
wasmFilePath: "".concat(tmpPath, "/semaphore.wasm"),
|
|
131
|
-
zkeyFilePath: "".concat(tmpPath, "/semaphore.zkey")
|
|
132
|
-
}];
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
});
|
|
49
|
+
async function getSnarkArtifacts(treeDepth) {
|
|
50
|
+
errorHandlers.requireNumber(treeDepth, "treeDepth");
|
|
51
|
+
const tmpDir = "semaphore-proof";
|
|
52
|
+
const tmpPath = `${os.tmpdir()}/${tmpDir}-${treeDepth}`;
|
|
53
|
+
if (!node_fs.existsSync(tmpPath) || node_fs.readdirSync(tmpPath).length !== 2) {
|
|
54
|
+
await download(`https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.wasm`, `${tmpPath}/semaphore.wasm`);
|
|
55
|
+
await download(`https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.zkey`, `${tmpPath}/semaphore.zkey`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
wasmFilePath: `${tmpPath}/semaphore.wasm`,
|
|
59
|
+
zkeyFilePath: `${tmpPath}/semaphore.zkey`
|
|
60
|
+
};
|
|
136
61
|
}
|
|
137
62
|
|
|
138
63
|
/**
|
|
@@ -175,89 +100,80 @@ function toBigInt(value) {
|
|
|
175
100
|
* or they will be automatically fetched.
|
|
176
101
|
* @param identity The Semaphore identity.
|
|
177
102
|
* @param groupOrMerkleProof The Semaphore group or its Merkle proof.
|
|
178
|
-
* @param scope The Semaphore scope.
|
|
179
103
|
* @param message The Semaphore message.
|
|
104
|
+
* @param scope The Semaphore scope.
|
|
180
105
|
* @param merkleTreeDepth The depth of the tree with which the circuit was compiled.
|
|
181
106
|
* @param snarkArtifacts The SNARK artifacts.
|
|
182
107
|
* @returns The Semaphore proof ready to be verified.
|
|
183
108
|
*/
|
|
184
|
-
function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
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
|
-
merkleTreeRoot: publicSignals[0],
|
|
253
|
-
nullifier: publicSignals[1],
|
|
254
|
-
message: message.toString(),
|
|
255
|
-
scope: scope.toString(),
|
|
256
|
-
points: proofPacking.packGroth16Proof(proof)
|
|
257
|
-
}];
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
});
|
|
109
|
+
async function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
110
|
+
errorHandlers.requireDefined(identity, "identity");
|
|
111
|
+
errorHandlers.requireDefined(groupOrMerkleProof, "groupOrMerkleProof");
|
|
112
|
+
errorHandlers.requireDefined(message, "message");
|
|
113
|
+
errorHandlers.requireDefined(scope, "scope");
|
|
114
|
+
errorHandlers.requireObject(identity, "identity");
|
|
115
|
+
errorHandlers.requireObject(groupOrMerkleProof, "groupOrMerkleProof");
|
|
116
|
+
errorHandlers.requireTypes(message, "message", ["string", "bigint", "number", "Uint8Array"]);
|
|
117
|
+
errorHandlers.requireTypes(scope, "scope", ["string", "bigint", "number", "Uint8Array"]);
|
|
118
|
+
if (merkleTreeDepth) {
|
|
119
|
+
errorHandlers.requireNumber(merkleTreeDepth, "merkleTreeDepth");
|
|
120
|
+
}
|
|
121
|
+
if (snarkArtifacts) {
|
|
122
|
+
errorHandlers.requireObject(snarkArtifacts, "snarkArtifacts");
|
|
123
|
+
}
|
|
124
|
+
// Message and scope can be strings, numbers or buffers (i.e. Uint8Array).
|
|
125
|
+
// They will be converted to bigints anyway.
|
|
126
|
+
message = toBigInt(message);
|
|
127
|
+
scope = toBigInt(scope);
|
|
128
|
+
let merkleProof;
|
|
129
|
+
// The second parameter can be either a Merkle proof or a group.
|
|
130
|
+
// If it is a group the Merkle proof will be calculated here.
|
|
131
|
+
if ("siblings" in groupOrMerkleProof) {
|
|
132
|
+
merkleProof = groupOrMerkleProof;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const leafIndex = groupOrMerkleProof.indexOf(identity.commitment);
|
|
136
|
+
merkleProof = groupOrMerkleProof.generateMerkleProof(leafIndex);
|
|
137
|
+
}
|
|
138
|
+
const merkleProofLength = merkleProof.siblings.length;
|
|
139
|
+
if (merkleTreeDepth !== undefined) {
|
|
140
|
+
if (merkleTreeDepth < constants.MIN_DEPTH || merkleTreeDepth > constants.MAX_DEPTH) {
|
|
141
|
+
throw new TypeError(`The tree depth must be a number between ${constants.MIN_DEPTH} and ${constants.MAX_DEPTH}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
merkleTreeDepth = merkleProofLength;
|
|
146
|
+
}
|
|
147
|
+
// If the Snark artifacts are not defined they will be automatically downloaded.
|
|
148
|
+
if (!snarkArtifacts) {
|
|
149
|
+
snarkArtifacts = await getSnarkArtifacts(merkleTreeDepth);
|
|
150
|
+
}
|
|
151
|
+
// The index must be converted to a list of indices, 1 for each tree level.
|
|
152
|
+
// The missing siblings can be set to 0, as they won't be used in the circuit.
|
|
153
|
+
const merkleProofIndices = [];
|
|
154
|
+
const merkleProofSiblings = merkleProof.siblings;
|
|
155
|
+
for (let i = 0; i < merkleTreeDepth; i += 1) {
|
|
156
|
+
merkleProofIndices.push((merkleProof.index >> i) & 1);
|
|
157
|
+
if (merkleProofSiblings[i] === undefined) {
|
|
158
|
+
merkleProofSiblings[i] = "0";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const { proof, publicSignals } = await snarkjs.groth16.fullProve({
|
|
162
|
+
secret: identity.secretScalar,
|
|
163
|
+
merkleProofLength,
|
|
164
|
+
merkleProofIndices,
|
|
165
|
+
merkleProofSiblings,
|
|
166
|
+
scope: hash(scope),
|
|
167
|
+
message: hash(message)
|
|
168
|
+
}, snarkArtifacts.wasmFilePath, snarkArtifacts.zkeyFilePath);
|
|
169
|
+
return {
|
|
170
|
+
merkleTreeDepth,
|
|
171
|
+
merkleTreeRoot: publicSignals[0],
|
|
172
|
+
nullifier: publicSignals[1],
|
|
173
|
+
message: message.toString(),
|
|
174
|
+
scope: scope.toString(),
|
|
175
|
+
points: proofPacking.packGroth16Proof(proof)
|
|
176
|
+
};
|
|
261
177
|
}
|
|
262
178
|
|
|
263
179
|
var protocol = "groth16";
|
|
@@ -840,26 +756,25 @@ var verificationKeys = {
|
|
|
840
756
|
* @param proof The Semaphore proof.
|
|
841
757
|
* @returns True if the proof is valid, false otherwise.
|
|
842
758
|
*/
|
|
843
|
-
function verifyProof(proof) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
});
|
|
759
|
+
async function verifyProof(proof) {
|
|
760
|
+
errorHandlers.requireDefined(proof, "proof");
|
|
761
|
+
errorHandlers.requireObject(proof, "proof");
|
|
762
|
+
const { merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points } = proof;
|
|
763
|
+
errorHandlers.requireNumber(merkleTreeDepth, "proof.merkleTreeDepth");
|
|
764
|
+
errorHandlers.requireString(merkleTreeRoot, "proof.merkleTreeRoot");
|
|
765
|
+
errorHandlers.requireString(nullifier, "proof.nullifier");
|
|
766
|
+
errorHandlers.requireString(message, "proof.message");
|
|
767
|
+
errorHandlers.requireString(scope, "proof.scope");
|
|
768
|
+
errorHandlers.requireArray(points, "proof.points");
|
|
769
|
+
if (merkleTreeDepth < constants.MIN_DEPTH || merkleTreeDepth > constants.MAX_DEPTH) {
|
|
770
|
+
throw new TypeError(`The tree depth must be a number between ${constants.MIN_DEPTH} and ${constants.MAX_DEPTH}`);
|
|
771
|
+
}
|
|
772
|
+
const verificationKey = {
|
|
773
|
+
...verificationKeys,
|
|
774
|
+
vk_delta_2: verificationKeys.vk_delta_2[merkleTreeDepth - 1],
|
|
775
|
+
IC: verificationKeys.IC[merkleTreeDepth - 1]
|
|
776
|
+
};
|
|
777
|
+
return snarkjs.groth16.verify(verificationKey, [merkleTreeRoot, nullifier, hash(message), hash(scope)], proofPacking.unpackGroth16Proof(points));
|
|
863
778
|
}
|
|
864
779
|
|
|
865
780
|
Object.defineProperty(exports, "packGroth16Proof", {
|
package/dist/index.node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @semaphore-protocol/proof
|
|
3
|
-
* @version 4.0.0-beta.
|
|
3
|
+
* @version 4.0.0-beta.6
|
|
4
4
|
* @file A library to generate and verify Semaphore proofs.
|
|
5
5
|
* @copyright Ethereum Foundation 2024
|
|
6
6
|
* @license MIT
|
|
@@ -21,85 +21,22 @@ import { keccak256 } from 'ethers/crypto';
|
|
|
21
21
|
import { toBeHex, toBigInt as toBigInt$1 } from 'ethers/utils';
|
|
22
22
|
import { encodeBytes32String } from 'ethers/abi';
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
__assign = Object.assign || function __assign(t) {
|
|
26
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
27
|
-
s = arguments[i];
|
|
28
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
|
-
return __assign.apply(this, arguments);
|
|
33
|
-
};
|
|
34
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
35
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
36
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
37
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
38
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
39
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
40
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
function __generator(thisArg, body) {
|
|
44
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
45
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
46
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
47
|
-
function step(op) {
|
|
48
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
49
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
50
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
51
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
52
|
-
switch (op[0]) {
|
|
53
|
-
case 0: case 1: t = op; break;
|
|
54
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
55
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
56
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
57
|
-
default:
|
|
58
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
59
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
60
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
61
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
62
|
-
if (t[2]) _.ops.pop();
|
|
63
|
-
_.trys.pop(); continue;
|
|
64
|
-
}
|
|
65
|
-
op = body.call(thisArg, _);
|
|
66
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
67
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
71
|
-
var e = new Error(message);
|
|
72
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
73
|
-
};
|
|
74
|
-
|
|
24
|
+
/* istanbul ignore file */
|
|
75
25
|
/**
|
|
76
26
|
* A utility function to download the zero-knowledge artifacts from an external server.
|
|
77
27
|
* @param url The URL from which to download the artifacts.
|
|
78
28
|
* @param outputPath The path in which to save the artifacts.
|
|
79
29
|
*/
|
|
80
|
-
function download(url, outputPath) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
dir = dirname(outputPath);
|
|
92
|
-
return [4 /*yield*/, mkdir(dir, { recursive: true })];
|
|
93
|
-
case 2:
|
|
94
|
-
_a.sent();
|
|
95
|
-
fileStream = createWriteStream(outputPath);
|
|
96
|
-
return [4 /*yield*/, finished(Readable.fromWeb(response.body).pipe(fileStream))];
|
|
97
|
-
case 3:
|
|
98
|
-
_a.sent();
|
|
99
|
-
return [2 /*return*/];
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
30
|
+
async function download(url, outputPath) {
|
|
31
|
+
const response = await fetch(url);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
// Ensure the directory exists.
|
|
36
|
+
const dir = dirname(outputPath);
|
|
37
|
+
await mkdir(dir, { recursive: true });
|
|
38
|
+
const fileStream = createWriteStream(outputPath);
|
|
39
|
+
await finished(Readable.fromWeb(response.body).pipe(fileStream));
|
|
103
40
|
}
|
|
104
41
|
/**
|
|
105
42
|
* Downloads the zero-knowledge artifacts and returns their paths.
|
|
@@ -108,30 +45,18 @@ function download(url, outputPath) {
|
|
|
108
45
|
* @param treeDepth The depth of the tree.
|
|
109
46
|
* @returns The zero-knowledge artifacts paths.
|
|
110
47
|
*/
|
|
111
|
-
function getSnarkArtifacts(treeDepth) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
_a.sent();
|
|
124
|
-
return [4 /*yield*/, download("https://semaphore.cedoor.dev/artifacts/".concat(treeDepth, "/semaphore.zkey"), "".concat(tmpPath, "/semaphore.zkey"))];
|
|
125
|
-
case 2:
|
|
126
|
-
_a.sent();
|
|
127
|
-
_a.label = 3;
|
|
128
|
-
case 3: return [2 /*return*/, {
|
|
129
|
-
wasmFilePath: "".concat(tmpPath, "/semaphore.wasm"),
|
|
130
|
-
zkeyFilePath: "".concat(tmpPath, "/semaphore.zkey")
|
|
131
|
-
}];
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
});
|
|
48
|
+
async function getSnarkArtifacts(treeDepth) {
|
|
49
|
+
requireNumber(treeDepth, "treeDepth");
|
|
50
|
+
const tmpDir = "semaphore-proof";
|
|
51
|
+
const tmpPath = `${os.tmpdir()}/${tmpDir}-${treeDepth}`;
|
|
52
|
+
if (!existsSync(tmpPath) || readdirSync(tmpPath).length !== 2) {
|
|
53
|
+
await download(`https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.wasm`, `${tmpPath}/semaphore.wasm`);
|
|
54
|
+
await download(`https://semaphore.cedoor.dev/artifacts/${treeDepth}/semaphore.zkey`, `${tmpPath}/semaphore.zkey`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
wasmFilePath: `${tmpPath}/semaphore.wasm`,
|
|
58
|
+
zkeyFilePath: `${tmpPath}/semaphore.zkey`
|
|
59
|
+
};
|
|
135
60
|
}
|
|
136
61
|
|
|
137
62
|
/**
|
|
@@ -174,89 +99,80 @@ function toBigInt(value) {
|
|
|
174
99
|
* or they will be automatically fetched.
|
|
175
100
|
* @param identity The Semaphore identity.
|
|
176
101
|
* @param groupOrMerkleProof The Semaphore group or its Merkle proof.
|
|
177
|
-
* @param scope The Semaphore scope.
|
|
178
102
|
* @param message The Semaphore message.
|
|
103
|
+
* @param scope The Semaphore scope.
|
|
179
104
|
* @param merkleTreeDepth The depth of the tree with which the circuit was compiled.
|
|
180
105
|
* @param snarkArtifacts The SNARK artifacts.
|
|
181
106
|
* @returns The Semaphore proof ready to be verified.
|
|
182
107
|
*/
|
|
183
|
-
function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
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
|
-
merkleTreeRoot: publicSignals[0],
|
|
252
|
-
nullifier: publicSignals[1],
|
|
253
|
-
message: message.toString(),
|
|
254
|
-
scope: scope.toString(),
|
|
255
|
-
points: packGroth16Proof(proof)
|
|
256
|
-
}];
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
});
|
|
108
|
+
async function generateProof(identity, groupOrMerkleProof, message, scope, merkleTreeDepth, snarkArtifacts) {
|
|
109
|
+
requireDefined(identity, "identity");
|
|
110
|
+
requireDefined(groupOrMerkleProof, "groupOrMerkleProof");
|
|
111
|
+
requireDefined(message, "message");
|
|
112
|
+
requireDefined(scope, "scope");
|
|
113
|
+
requireObject(identity, "identity");
|
|
114
|
+
requireObject(groupOrMerkleProof, "groupOrMerkleProof");
|
|
115
|
+
requireTypes(message, "message", ["string", "bigint", "number", "Uint8Array"]);
|
|
116
|
+
requireTypes(scope, "scope", ["string", "bigint", "number", "Uint8Array"]);
|
|
117
|
+
if (merkleTreeDepth) {
|
|
118
|
+
requireNumber(merkleTreeDepth, "merkleTreeDepth");
|
|
119
|
+
}
|
|
120
|
+
if (snarkArtifacts) {
|
|
121
|
+
requireObject(snarkArtifacts, "snarkArtifacts");
|
|
122
|
+
}
|
|
123
|
+
// Message and scope can be strings, numbers or buffers (i.e. Uint8Array).
|
|
124
|
+
// They will be converted to bigints anyway.
|
|
125
|
+
message = toBigInt(message);
|
|
126
|
+
scope = toBigInt(scope);
|
|
127
|
+
let merkleProof;
|
|
128
|
+
// The second parameter can be either a Merkle proof or a group.
|
|
129
|
+
// If it is a group the Merkle proof will be calculated here.
|
|
130
|
+
if ("siblings" in groupOrMerkleProof) {
|
|
131
|
+
merkleProof = groupOrMerkleProof;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const leafIndex = groupOrMerkleProof.indexOf(identity.commitment);
|
|
135
|
+
merkleProof = groupOrMerkleProof.generateMerkleProof(leafIndex);
|
|
136
|
+
}
|
|
137
|
+
const merkleProofLength = merkleProof.siblings.length;
|
|
138
|
+
if (merkleTreeDepth !== undefined) {
|
|
139
|
+
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
|
|
140
|
+
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
merkleTreeDepth = merkleProofLength;
|
|
145
|
+
}
|
|
146
|
+
// If the Snark artifacts are not defined they will be automatically downloaded.
|
|
147
|
+
if (!snarkArtifacts) {
|
|
148
|
+
snarkArtifacts = await getSnarkArtifacts(merkleTreeDepth);
|
|
149
|
+
}
|
|
150
|
+
// The index must be converted to a list of indices, 1 for each tree level.
|
|
151
|
+
// The missing siblings can be set to 0, as they won't be used in the circuit.
|
|
152
|
+
const merkleProofIndices = [];
|
|
153
|
+
const merkleProofSiblings = merkleProof.siblings;
|
|
154
|
+
for (let i = 0; i < merkleTreeDepth; i += 1) {
|
|
155
|
+
merkleProofIndices.push((merkleProof.index >> i) & 1);
|
|
156
|
+
if (merkleProofSiblings[i] === undefined) {
|
|
157
|
+
merkleProofSiblings[i] = "0";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const { proof, publicSignals } = await groth16.fullProve({
|
|
161
|
+
secret: identity.secretScalar,
|
|
162
|
+
merkleProofLength,
|
|
163
|
+
merkleProofIndices,
|
|
164
|
+
merkleProofSiblings,
|
|
165
|
+
scope: hash(scope),
|
|
166
|
+
message: hash(message)
|
|
167
|
+
}, snarkArtifacts.wasmFilePath, snarkArtifacts.zkeyFilePath);
|
|
168
|
+
return {
|
|
169
|
+
merkleTreeDepth,
|
|
170
|
+
merkleTreeRoot: publicSignals[0],
|
|
171
|
+
nullifier: publicSignals[1],
|
|
172
|
+
message: message.toString(),
|
|
173
|
+
scope: scope.toString(),
|
|
174
|
+
points: packGroth16Proof(proof)
|
|
175
|
+
};
|
|
260
176
|
}
|
|
261
177
|
|
|
262
178
|
var protocol = "groth16";
|
|
@@ -839,26 +755,25 @@ var verificationKeys = {
|
|
|
839
755
|
* @param proof The Semaphore proof.
|
|
840
756
|
* @returns True if the proof is valid, false otherwise.
|
|
841
757
|
*/
|
|
842
|
-
function verifyProof(proof) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
});
|
|
758
|
+
async function verifyProof(proof) {
|
|
759
|
+
requireDefined(proof, "proof");
|
|
760
|
+
requireObject(proof, "proof");
|
|
761
|
+
const { merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points } = proof;
|
|
762
|
+
requireNumber(merkleTreeDepth, "proof.merkleTreeDepth");
|
|
763
|
+
requireString(merkleTreeRoot, "proof.merkleTreeRoot");
|
|
764
|
+
requireString(nullifier, "proof.nullifier");
|
|
765
|
+
requireString(message, "proof.message");
|
|
766
|
+
requireString(scope, "proof.scope");
|
|
767
|
+
requireArray(points, "proof.points");
|
|
768
|
+
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
|
|
769
|
+
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`);
|
|
770
|
+
}
|
|
771
|
+
const verificationKey = {
|
|
772
|
+
...verificationKeys,
|
|
773
|
+
vk_delta_2: verificationKeys.vk_delta_2[merkleTreeDepth - 1],
|
|
774
|
+
IC: verificationKeys.IC[merkleTreeDepth - 1]
|
|
775
|
+
};
|
|
776
|
+
return groth16.verify(verificationKey, [merkleTreeRoot, nullifier, hash(message), hash(scope)], unpackGroth16Proof(points));
|
|
862
777
|
}
|
|
863
778
|
|
|
864
779
|
export { generateProof, verifyProof };
|
|
@@ -16,8 +16,8 @@ import { SemaphoreProof, SnarkArtifacts } from "./types";
|
|
|
16
16
|
* or they will be automatically fetched.
|
|
17
17
|
* @param identity The Semaphore identity.
|
|
18
18
|
* @param groupOrMerkleProof The Semaphore group or its Merkle proof.
|
|
19
|
-
* @param scope The Semaphore scope.
|
|
20
19
|
* @param message The Semaphore message.
|
|
20
|
+
* @param scope The Semaphore scope.
|
|
21
21
|
* @param merkleTreeDepth The depth of the tree with which the circuit was compiled.
|
|
22
22
|
* @param snarkArtifacts The SNARK artifacts.
|
|
23
23
|
* @returns The Semaphore proof ready to be verified.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semaphore-protocol/proof",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.6",
|
|
4
4
|
"description": "A library to generate and verify Semaphore proofs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,19 +48,13 @@
|
|
|
48
48
|
"rollup-plugin-cleanup": "^3.2.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@semaphore-protocol/group": "4.0.0-beta.
|
|
52
|
-
"@semaphore-protocol/identity": "4.0.0-beta.
|
|
51
|
+
"@semaphore-protocol/group": "4.0.0-beta.6",
|
|
52
|
+
"@semaphore-protocol/identity": "4.0.0-beta.6"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@semaphore-protocol/utils": "4.0.0-beta.
|
|
55
|
+
"@semaphore-protocol/utils": "4.0.0-beta.6",
|
|
56
56
|
"@zk-kit/utils": "0.8.1",
|
|
57
57
|
"ethers": "6.10.0",
|
|
58
58
|
"snarkjs": "0.7.3"
|
|
59
|
-
},
|
|
60
|
-
"resolutions": {
|
|
61
|
-
"web-worker": "1.2.0"
|
|
62
|
-
},
|
|
63
|
-
"overrides": {
|
|
64
|
-
"web-worker": "1.2.0"
|
|
65
59
|
}
|
|
66
60
|
}
|
package/src/generate-proof.ts
CHANGED
|
@@ -24,8 +24,8 @@ import { SemaphoreProof, SnarkArtifacts } from "./types"
|
|
|
24
24
|
* or they will be automatically fetched.
|
|
25
25
|
* @param identity The Semaphore identity.
|
|
26
26
|
* @param groupOrMerkleProof The Semaphore group or its Merkle proof.
|
|
27
|
-
* @param scope The Semaphore scope.
|
|
28
27
|
* @param message The Semaphore message.
|
|
28
|
+
* @param scope The Semaphore scope.
|
|
29
29
|
* @param merkleTreeDepth The depth of the tree with which the circuit was compiled.
|
|
30
30
|
* @param snarkArtifacts The SNARK artifacts.
|
|
31
31
|
* @returns The Semaphore proof ready to be verified.
|