0, // dummy
281 | fd_fdstat_get: () => 0, // dummy
282 | fd_seek: () => 0, // dummy
283 | "proc_exit": (code) => {
284 | if (global.process) {
285 | // Node.js
286 | process.exit(code);
287 | } else {
288 | // Can't exit in a browser.
289 | throw 'trying to exit with code ' + code;
290 | }
291 | },
292 | random_get: (bufPtr, bufLen) => {
293 | crypto.getRandomValues(loadSlice(bufPtr, bufLen));
294 | return 0;
295 | },
296 | },
297 | env: {
298 | // func ticks() float64
299 | "runtime.ticks": () => {
300 | return timeOrigin + performance.now();
301 | },
302 |
303 | // func sleepTicks(timeout float64)
304 | "runtime.sleepTicks": (timeout) => {
305 | // Do not sleep, only reactivate scheduler after the given timeout.
306 | setTimeout(this._inst.exports.go_scheduler, timeout);
307 | },
308 |
309 | // func finalizeRef(v ref)
310 | "syscall/js.finalizeRef": (sp) => {
311 | // Note: TinyGo does not support finalizers so this should never be
312 | // called.
313 | console.error('syscall/js.finalizeRef not implemented');
314 | },
315 |
316 | // func stringVal(value string) ref
317 | "syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => {
318 | const s = loadString(value_ptr, value_len);
319 | storeValue(ret_ptr, s);
320 | },
321 |
322 | // func valueGet(v ref, p string) ref
323 | "syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => {
324 | let prop = loadString(p_ptr, p_len);
325 | let value = loadValue(v_addr);
326 | let result = Reflect.get(value, prop);
327 | storeValue(retval, result);
328 | },
329 |
330 | // func valueSet(v ref, p string, x ref)
331 | "syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => {
332 | const v = loadValue(v_addr);
333 | const p = loadString(p_ptr, p_len);
334 | const x = loadValue(x_addr);
335 | Reflect.set(v, p, x);
336 | },
337 |
338 | // func valueDelete(v ref, p string)
339 | "syscall/js.valueDelete": (v_addr, p_ptr, p_len) => {
340 | const v = loadValue(v_addr);
341 | const p = loadString(p_ptr, p_len);
342 | Reflect.deleteProperty(v, p);
343 | },
344 |
345 | // func valueIndex(v ref, i int) ref
346 | "syscall/js.valueIndex": (ret_addr, v_addr, i) => {
347 | storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
348 | },
349 |
350 | // valueSetIndex(v ref, i int, x ref)
351 | "syscall/js.valueSetIndex": (v_addr, i, x_addr) => {
352 | Reflect.set(loadValue(v_addr), i, loadValue(x_addr));
353 | },
354 |
355 | // func valueCall(v ref, m string, args []ref) (ref, bool)
356 | "syscall/js.valueCall": (ret_addr, v_addr, m_ptr, m_len, args_ptr, args_len, args_cap) => {
357 | const v = loadValue(v_addr);
358 | const name = loadString(m_ptr, m_len);
359 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
360 | try {
361 | const m = Reflect.get(v, name);
362 | storeValue(ret_addr, Reflect.apply(m, v, args));
363 | mem().setUint8(ret_addr + 8, 1);
364 | } catch (err) {
365 | storeValue(ret_addr, err);
366 | mem().setUint8(ret_addr + 8, 0);
367 | }
368 | },
369 |
370 | // func valueInvoke(v ref, args []ref) (ref, bool)
371 | "syscall/js.valueInvoke": (ret_addr, v_addr, args_ptr, args_len, args_cap) => {
372 | try {
373 | const v = loadValue(v_addr);
374 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
375 | storeValue(ret_addr, Reflect.apply(v, undefined, args));
376 | mem().setUint8(ret_addr + 8, 1);
377 | } catch (err) {
378 | storeValue(ret_addr, err);
379 | mem().setUint8(ret_addr + 8, 0);
380 | }
381 | },
382 |
383 | // func valueNew(v ref, args []ref) (ref, bool)
384 | "syscall/js.valueNew": (ret_addr, v_addr, args_ptr, args_len, args_cap) => {
385 | const v = loadValue(v_addr);
386 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
387 | try {
388 | storeValue(ret_addr, Reflect.construct(v, args));
389 | mem().setUint8(ret_addr + 8, 1);
390 | } catch (err) {
391 | storeValue(ret_addr, err);
392 | mem().setUint8(ret_addr+ 8, 0);
393 | }
394 | },
395 |
396 | // func valueLength(v ref) int
397 | "syscall/js.valueLength": (v_addr) => {
398 | return loadValue(v_addr).length;
399 | },
400 |
401 | // valuePrepareString(v ref) (ref, int)
402 | "syscall/js.valuePrepareString": (ret_addr, v_addr) => {
403 | const s = String(loadValue(v_addr));
404 | const str = encoder.encode(s);
405 | storeValue(ret_addr, str);
406 | setInt64(ret_addr + 8, str.length);
407 | },
408 |
409 | // valueLoadString(v ref, b []byte)
410 | "syscall/js.valueLoadString": (v_addr, slice_ptr, slice_len, slice_cap) => {
411 | const str = loadValue(v_addr);
412 | loadSlice(slice_ptr, slice_len, slice_cap).set(str);
413 | },
414 |
415 | // func valueInstanceOf(v ref, t ref) bool
416 | "syscall/js.valueInstanceOf": (v_addr, t_addr) => {
417 | return loadValue(v_addr) instanceof loadValue(t_addr);
418 | },
419 |
420 | // func copyBytesToGo(dst []byte, src ref) (int, bool)
421 | "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, source_addr) => {
422 | let num_bytes_copied_addr = ret_addr;
423 | let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
424 |
425 | const dst = loadSlice(dest_addr, dest_len);
426 | const src = loadValue(source_addr);
427 | if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
428 | mem().setUint8(returned_status_addr, 0); // Return "not ok" status
429 | return;
430 | }
431 | const toCopy = src.subarray(0, dst.length);
432 | dst.set(toCopy);
433 | setInt64(num_bytes_copied_addr, toCopy.length);
434 | mem().setUint8(returned_status_addr, 1); // Return "ok" status
435 | },
436 |
437 | // copyBytesToJS(dst ref, src []byte) (int, bool)
438 | // Originally copied from upstream Go project, then modified:
439 | // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
440 | "syscall/js.copyBytesToJS": (ret_addr, dest_addr, source_addr, source_len, source_cap) => {
441 | let num_bytes_copied_addr = ret_addr;
442 | let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
443 |
444 | const dst = loadValue(dest_addr);
445 | const src = loadSlice(source_addr, source_len);
446 | if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
447 | mem().setUint8(returned_status_addr, 0); // Return "not ok" status
448 | return;
449 | }
450 | const toCopy = src.subarray(0, dst.length);
451 | dst.set(toCopy);
452 | setInt64(num_bytes_copied_addr, toCopy.length);
453 | mem().setUint8(returned_status_addr, 1); // Return "ok" status
454 | },
455 | }
456 | };
457 | }
458 |
459 | async run(instance) {
460 | this._inst = instance;
461 | this._values = [ // JS values that Go currently has references to, indexed by reference id
462 | NaN,
463 | 0,
464 | null,
465 | true,
466 | false,
467 | global,
468 | this,
469 | ];
470 | this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
471 | this._ids = new Map(); // mapping from JS values to reference ids
472 | this._idPool = []; // unused ids that have been garbage collected
473 | this.exited = false; // whether the Go program has exited
474 |
475 | const mem = new DataView(this._inst.exports.memory.buffer)
476 |
477 | while (true) {
478 | const callbackPromise = new Promise((resolve) => {
479 | this._resolveCallbackPromise = () => {
480 | if (this.exited) {
481 | throw new Error("bad callback: Go program has already exited");
482 | }
483 | setTimeout(resolve, 0); // make sure it is asynchronous
484 | };
485 | });
486 | this._inst.exports._start();
487 | if (this.exited) {
488 | break;
489 | }
490 | await callbackPromise;
491 | }
492 | }
493 |
494 | _resume() {
495 | if (this.exited) {
496 | throw new Error("Go program has already exited");
497 | }
498 | this._inst.exports.resume();
499 | if (this.exited) {
500 | this._resolveExitPromise();
501 | }
502 | }
503 |
504 | _makeFuncWrapper(id) {
505 | const go = this;
506 | return function () {
507 | const event = { id: id, this: this, args: arguments };
508 | go._pendingEvent = event;
509 | go._resume();
510 | return event.result;
511 | };
512 | }
513 | }
514 |
515 | if (
516 | global.require &&
517 | global.require.main === module &&
518 | global.process &&
519 | global.process.versions &&
520 | !global.process.versions.electron
521 | ) {
522 | if (process.argv.length != 3) {
523 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
524 | process.exit(1);
525 | }
526 |
527 | const go = new Go();
528 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
529 | return go.run(result.instance);
530 | }).catch((err) => {
531 | console.error(err);
532 | process.exit(1);
533 | });
534 | }
535 | })();
536 |
--------------------------------------------------------------------------------
/examples/tinygowasi/worker.js:
--------------------------------------------------------------------------------
1 | import './wasm_exec.js';
2 |
3 | import { init, WASI } from '@wasmer/wasi';
4 |
5 | import witnessUrl from "url:./artifacts/zkcensus.witness";
6 | import wasmUrl from "url:./artifacts/g16_prover.wasm";
7 |
8 | addEventListener('message', async(e) => {
9 | await init();
10 |
11 | let start = Date.now();
12 | console.log("reading artifacts...");
13 |
14 | let resWitness = await fetch(witnessUrl);
15 | let witnessBuff = await resWitness.arrayBuffer();
16 | let witness = new Uint8Array(witnessBuff);
17 |
18 | console.log("loading go env and wasm...");
19 | let wasi = new WASI({
20 | env: {},
21 | args: [
22 | `[${witness.join(",")}]`,
23 | ],
24 | });
25 | const wasmModule = await WebAssembly.compileStreaming(fetch(wasmUrl));
26 | const instance = await wasi.instantiate(wasmModule, {
27 | ...wasi.getImports(wasmModule)
28 | });
29 | console.log(instance)
30 |
31 | console.log("generating proof...");
32 | let status = wasi.start();
33 | console.log("status code", status);
34 |
35 | let res = wasi.getStdoutString();
36 | console.log("result", res);
37 |
38 | let end = Date.now();
39 | let elapsed = end - start;
40 | console.log("Finished!", `${ elapsed / 1000 }s`);
41 | });
--------------------------------------------------------------------------------
/examples/web/index_g16.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Gnark ZkCensus-Wasm prover test | Vocdoni
6 |
7 |
20 |
21 |
22 |
23 |
24 | Gnark ZkCensus-Wasm prover test
25 |
26 |
27 | ⚠️🚧 This repository is currently a proof of concept. 🚧⚠️
28 |
29 |
30 |
31 |
32 |
61 |
62 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/examples/web/index_plonk.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Gnark ZkCensus-Wasm prover test | Vocdoni
6 |
7 |
20 |
21 |
22 |
23 |
24 | Gnark ZkCensus-Wasm prover test
25 |
26 |
27 | ⚠️🚧 This repository is currently a proof of concept. 🚧⚠️
28 |
29 |
30 |
31 |
32 |
61 |
62 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/examples/web/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "path/filepath"
8 | "runtime"
9 | "strings"
10 | )
11 |
12 | func main() {
13 | _, callerFile, _, ok := runtime.Caller(0)
14 | if !ok {
15 | fmt.Println("unable to get the current caller file")
16 | return
17 | }
18 | examplePath := filepath.Dir(callerFile)
19 | artifactsPath := filepath.Join(examplePath, "../../artifacts")
20 |
21 | exampleFs := http.FileServer(http.Dir(examplePath))
22 | artifactsFs := http.FileServer(http.Dir(artifactsPath))
23 |
24 | artifactsHandler := http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
25 | resp.Header().Add("Cache-Control", "no-cache")
26 | if strings.HasSuffix(req.URL.Path, ".wasm") {
27 | resp.Header().Set("content-type", "application/wasm")
28 | }
29 | artifactsFs.ServeHTTP(resp, req)
30 | log.Println("artifactsHandler: ", req.URL.Path)
31 | })
32 |
33 | http.Handle("/", exampleFs)
34 | http.Handle("/artifacts/", http.StripPrefix("/artifacts/", artifactsHandler))
35 |
36 | fmt.Println("Starting http server... Example url: http://0.0.0.0:5050/. Check the console!")
37 | if err := http.ListenAndServe(":5050", nil); err != nil {
38 | fmt.Println(err)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/web/wasm_exec.js:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 | //
5 | // This file has been modified for use by the TinyGo compiler.
6 |
7 | (() => {
8 | // Map multiple JavaScript environments to a single common API,
9 | // preferring web standards over Node.js API.
10 | //
11 | // Environments considered:
12 | // - Browsers
13 | // - Node.js
14 | // - Electron
15 | // - Parcel
16 |
17 | if (typeof global !== "undefined") {
18 | // global already exists
19 | } else if (typeof window !== "undefined") {
20 | window.global = window;
21 | } else if (typeof self !== "undefined") {
22 | self.global = self;
23 | } else {
24 | throw new Error("cannot export Go (neither global, window nor self is defined)");
25 | }
26 |
27 | if (!global.require && typeof require !== "undefined") {
28 | global.require = require;
29 | }
30 |
31 | if (!global.fs && global.require) {
32 | global.fs = require("fs");
33 | }
34 |
35 | const enosys = () => {
36 | const err = new Error("not implemented");
37 | err.code = "ENOSYS";
38 | return err;
39 | };
40 |
41 | if (!global.fs) {
42 | let outputBuf = "";
43 | global.fs = {
44 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
45 | writeSync(fd, buf) {
46 | outputBuf += decoder.decode(buf);
47 | const nl = outputBuf.lastIndexOf("\n");
48 | if (nl != -1) {
49 | console.log(outputBuf.substr(0, nl));
50 | outputBuf = outputBuf.substr(nl + 1);
51 | }
52 | return buf.length;
53 | },
54 | write(fd, buf, offset, length, position, callback) {
55 | if (offset !== 0 || length !== buf.length || position !== null) {
56 | callback(enosys());
57 | return;
58 | }
59 | const n = this.writeSync(fd, buf);
60 | callback(null, n);
61 | },
62 | chmod(path, mode, callback) { callback(enosys()); },
63 | chown(path, uid, gid, callback) { callback(enosys()); },
64 | close(fd, callback) { callback(enosys()); },
65 | fchmod(fd, mode, callback) { callback(enosys()); },
66 | fchown(fd, uid, gid, callback) { callback(enosys()); },
67 | fstat(fd, callback) { callback(enosys()); },
68 | fsync(fd, callback) { callback(null); },
69 | ftruncate(fd, length, callback) { callback(enosys()); },
70 | lchown(path, uid, gid, callback) { callback(enosys()); },
71 | link(path, link, callback) { callback(enosys()); },
72 | lstat(path, callback) { callback(enosys()); },
73 | mkdir(path, perm, callback) { callback(enosys()); },
74 | open(path, flags, mode, callback) { callback(enosys()); },
75 | read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
76 | readdir(path, callback) { callback(enosys()); },
77 | readlink(path, callback) { callback(enosys()); },
78 | rename(from, to, callback) { callback(enosys()); },
79 | rmdir(path, callback) { callback(enosys()); },
80 | stat(path, callback) { callback(enosys()); },
81 | symlink(path, link, callback) { callback(enosys()); },
82 | truncate(path, length, callback) { callback(enosys()); },
83 | unlink(path, callback) { callback(enosys()); },
84 | utimes(path, atime, mtime, callback) { callback(enosys()); },
85 | };
86 | }
87 |
88 | if (!global.process) {
89 | global.process = {
90 | getuid() { return -1; },
91 | getgid() { return -1; },
92 | geteuid() { return -1; },
93 | getegid() { return -1; },
94 | getgroups() { throw enosys(); },
95 | pid: -1,
96 | ppid: -1,
97 | umask() { throw enosys(); },
98 | cwd() { throw enosys(); },
99 | chdir() { throw enosys(); },
100 | }
101 | }
102 |
103 | if (!global.crypto) {
104 | const nodeCrypto = require("crypto");
105 | global.crypto = {
106 | getRandomValues(b) {
107 | nodeCrypto.randomFillSync(b);
108 | },
109 | };
110 | }
111 |
112 | if (!global.performance) {
113 | global.performance = {
114 | now() {
115 | const [sec, nsec] = process.hrtime();
116 | return sec * 1000 + nsec / 1000000;
117 | },
118 | };
119 | }
120 |
121 | if (!global.TextEncoder) {
122 | global.TextEncoder = require("util").TextEncoder;
123 | }
124 |
125 | if (!global.TextDecoder) {
126 | global.TextDecoder = require("util").TextDecoder;
127 | }
128 |
129 | // End of polyfills for common API.
130 |
131 | const encoder = new TextEncoder("utf-8");
132 | const decoder = new TextDecoder("utf-8");
133 | var logLine = [];
134 |
135 | global.Go = class {
136 | constructor() {
137 | this._callbackTimeouts = new Map();
138 | this._nextCallbackTimeoutID = 1;
139 |
140 | const mem = () => {
141 | // The buffer may change when requesting more memory.
142 | return new DataView(this._inst.exports.memory.buffer);
143 | }
144 |
145 | const setInt64 = (addr, v) => {
146 | mem().setUint32(addr + 0, v, true);
147 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
148 | }
149 |
150 | const getInt64 = (addr) => {
151 | const low = mem().getUint32(addr + 0, true);
152 | const high = mem().getInt32(addr + 4, true);
153 | return low + high * 4294967296;
154 | }
155 |
156 | const loadValue = (addr) => {
157 | const f = mem().getFloat64(addr, true);
158 | if (f === 0) {
159 | return undefined;
160 | }
161 | if (!isNaN(f)) {
162 | return f;
163 | }
164 |
165 | const id = mem().getUint32(addr, true);
166 | return this._values[id];
167 | }
168 |
169 | const storeValue = (addr, v) => {
170 | const nanHead = 0x7FF80000;
171 |
172 | if (typeof v === "number") {
173 | if (isNaN(v)) {
174 | mem().setUint32(addr + 4, nanHead, true);
175 | mem().setUint32(addr, 0, true);
176 | return;
177 | }
178 | if (v === 0) {
179 | mem().setUint32(addr + 4, nanHead, true);
180 | mem().setUint32(addr, 1, true);
181 | return;
182 | }
183 | mem().setFloat64(addr, v, true);
184 | return;
185 | }
186 |
187 | switch (v) {
188 | case undefined:
189 | mem().setFloat64(addr, 0, true);
190 | return;
191 | case null:
192 | mem().setUint32(addr + 4, nanHead, true);
193 | mem().setUint32(addr, 2, true);
194 | return;
195 | case true:
196 | mem().setUint32(addr + 4, nanHead, true);
197 | mem().setUint32(addr, 3, true);
198 | return;
199 | case false:
200 | mem().setUint32(addr + 4, nanHead, true);
201 | mem().setUint32(addr, 4, true);
202 | return;
203 | }
204 |
205 | let id = this._ids.get(v);
206 | if (id === undefined) {
207 | id = this._idPool.pop();
208 | if (id === undefined) {
209 | id = this._values.length;
210 | }
211 | this._values[id] = v;
212 | this._goRefCounts[id] = 0;
213 | this._ids.set(v, id);
214 | }
215 | this._goRefCounts[id]++;
216 | let typeFlag = 1;
217 | switch (typeof v) {
218 | case "string":
219 | typeFlag = 2;
220 | break;
221 | case "symbol":
222 | typeFlag = 3;
223 | break;
224 | case "function":
225 | typeFlag = 4;
226 | break;
227 | }
228 | mem().setUint32(addr + 4, nanHead | typeFlag, true);
229 | mem().setUint32(addr, id, true);
230 | }
231 |
232 | const loadSlice = (array, len, cap) => {
233 | return new Uint8Array(this._inst.exports.memory.buffer, array, len);
234 | }
235 |
236 | const loadSliceOfValues = (array, len, cap) => {
237 | const a = new Array(len);
238 | for (let i = 0; i < len; i++) {
239 | a[i] = loadValue(array + i * 8);
240 | }
241 | return a;
242 | }
243 |
244 | const loadString = (ptr, len) => {
245 | return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
246 | }
247 |
248 | const timeOrigin = Date.now() - performance.now();
249 | this.importObject = {
250 | wasi_snapshot_preview1: {
251 | // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
252 | fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
253 | let nwritten = 0;
254 | if (fd == 1) {
255 | for (let iovs_i=0; iovs_i 0, // dummy
281 | fd_fdstat_get: () => 0, // dummy
282 | fd_seek: () => 0, // dummy
283 | "proc_exit": (code) => {
284 | if (global.process) {
285 | // Node.js
286 | process.exit(code);
287 | } else {
288 | // Can't exit in a browser.
289 | throw 'trying to exit with code ' + code;
290 | }
291 | },
292 | random_get: (bufPtr, bufLen) => {
293 | crypto.getRandomValues(loadSlice(bufPtr, bufLen));
294 | return 0;
295 | },
296 | },
297 | env: {
298 | // func ticks() float64
299 | "runtime.ticks": () => {
300 | return timeOrigin + performance.now();
301 | },
302 |
303 | // func sleepTicks(timeout float64)
304 | "runtime.sleepTicks": (timeout) => {
305 | // Do not sleep, only reactivate scheduler after the given timeout.
306 | setTimeout(this._inst.exports.go_scheduler, timeout);
307 | },
308 |
309 | // func finalizeRef(v ref)
310 | "syscall/js.finalizeRef": (sp) => {
311 | // Note: TinyGo does not support finalizers so this should never be
312 | // called.
313 | console.error('syscall/js.finalizeRef not implemented');
314 | },
315 |
316 | // func stringVal(value string) ref
317 | "syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => {
318 | const s = loadString(value_ptr, value_len);
319 | storeValue(ret_ptr, s);
320 | },
321 |
322 | // func valueGet(v ref, p string) ref
323 | "syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => {
324 | let prop = loadString(p_ptr, p_len);
325 | let value = loadValue(v_addr);
326 | let result = Reflect.get(value, prop);
327 | storeValue(retval, result);
328 | },
329 |
330 | // func valueSet(v ref, p string, x ref)
331 | "syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => {
332 | const v = loadValue(v_addr);
333 | const p = loadString(p_ptr, p_len);
334 | const x = loadValue(x_addr);
335 | Reflect.set(v, p, x);
336 | },
337 |
338 | // func valueDelete(v ref, p string)
339 | "syscall/js.valueDelete": (v_addr, p_ptr, p_len) => {
340 | const v = loadValue(v_addr);
341 | const p = loadString(p_ptr, p_len);
342 | Reflect.deleteProperty(v, p);
343 | },
344 |
345 | // func valueIndex(v ref, i int) ref
346 | "syscall/js.valueIndex": (ret_addr, v_addr, i) => {
347 | storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
348 | },
349 |
350 | // valueSetIndex(v ref, i int, x ref)
351 | "syscall/js.valueSetIndex": (v_addr, i, x_addr) => {
352 | Reflect.set(loadValue(v_addr), i, loadValue(x_addr));
353 | },
354 |
355 | // func valueCall(v ref, m string, args []ref) (ref, bool)
356 | "syscall/js.valueCall": (ret_addr, v_addr, m_ptr, m_len, args_ptr, args_len, args_cap) => {
357 | const v = loadValue(v_addr);
358 | const name = loadString(m_ptr, m_len);
359 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
360 | try {
361 | const m = Reflect.get(v, name);
362 | storeValue(ret_addr, Reflect.apply(m, v, args));
363 | mem().setUint8(ret_addr + 8, 1);
364 | } catch (err) {
365 | storeValue(ret_addr, err);
366 | mem().setUint8(ret_addr + 8, 0);
367 | }
368 | },
369 |
370 | // func valueInvoke(v ref, args []ref) (ref, bool)
371 | "syscall/js.valueInvoke": (ret_addr, v_addr, args_ptr, args_len, args_cap) => {
372 | try {
373 | const v = loadValue(v_addr);
374 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
375 | storeValue(ret_addr, Reflect.apply(v, undefined, args));
376 | mem().setUint8(ret_addr + 8, 1);
377 | } catch (err) {
378 | storeValue(ret_addr, err);
379 | mem().setUint8(ret_addr + 8, 0);
380 | }
381 | },
382 |
383 | // func valueNew(v ref, args []ref) (ref, bool)
384 | "syscall/js.valueNew": (ret_addr, v_addr, args_ptr, args_len, args_cap) => {
385 | const v = loadValue(v_addr);
386 | const args = loadSliceOfValues(args_ptr, args_len, args_cap);
387 | try {
388 | storeValue(ret_addr, Reflect.construct(v, args));
389 | mem().setUint8(ret_addr + 8, 1);
390 | } catch (err) {
391 | storeValue(ret_addr, err);
392 | mem().setUint8(ret_addr+ 8, 0);
393 | }
394 | },
395 |
396 | // func valueLength(v ref) int
397 | "syscall/js.valueLength": (v_addr) => {
398 | return loadValue(v_addr).length;
399 | },
400 |
401 | // valuePrepareString(v ref) (ref, int)
402 | "syscall/js.valuePrepareString": (ret_addr, v_addr) => {
403 | const s = String(loadValue(v_addr));
404 | const str = encoder.encode(s);
405 | storeValue(ret_addr, str);
406 | setInt64(ret_addr + 8, str.length);
407 | },
408 |
409 | // valueLoadString(v ref, b []byte)
410 | "syscall/js.valueLoadString": (v_addr, slice_ptr, slice_len, slice_cap) => {
411 | const str = loadValue(v_addr);
412 | loadSlice(slice_ptr, slice_len, slice_cap).set(str);
413 | },
414 |
415 | // func valueInstanceOf(v ref, t ref) bool
416 | "syscall/js.valueInstanceOf": (v_addr, t_addr) => {
417 | return loadValue(v_addr) instanceof loadValue(t_addr);
418 | },
419 |
420 | // func copyBytesToGo(dst []byte, src ref) (int, bool)
421 | "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, source_addr) => {
422 | let num_bytes_copied_addr = ret_addr;
423 | let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
424 |
425 | const dst = loadSlice(dest_addr, dest_len);
426 | const src = loadValue(source_addr);
427 | if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
428 | mem().setUint8(returned_status_addr, 0); // Return "not ok" status
429 | return;
430 | }
431 | const toCopy = src.subarray(0, dst.length);
432 | dst.set(toCopy);
433 | setInt64(num_bytes_copied_addr, toCopy.length);
434 | mem().setUint8(returned_status_addr, 1); // Return "ok" status
435 | },
436 |
437 | // copyBytesToJS(dst ref, src []byte) (int, bool)
438 | // Originally copied from upstream Go project, then modified:
439 | // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
440 | "syscall/js.copyBytesToJS": (ret_addr, dest_addr, source_addr, source_len, source_cap) => {
441 | let num_bytes_copied_addr = ret_addr;
442 | let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
443 |
444 | const dst = loadValue(dest_addr);
445 | const src = loadSlice(source_addr, source_len);
446 | if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
447 | mem().setUint8(returned_status_addr, 0); // Return "not ok" status
448 | return;
449 | }
450 | const toCopy = src.subarray(0, dst.length);
451 | dst.set(toCopy);
452 | setInt64(num_bytes_copied_addr, toCopy.length);
453 | mem().setUint8(returned_status_addr, 1); // Return "ok" status
454 | },
455 | }
456 | };
457 | }
458 |
459 | async run(instance) {
460 | this._inst = instance;
461 | this._values = [ // JS values that Go currently has references to, indexed by reference id
462 | NaN,
463 | 0,
464 | null,
465 | true,
466 | false,
467 | global,
468 | this,
469 | ];
470 | this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
471 | this._ids = new Map(); // mapping from JS values to reference ids
472 | this._idPool = []; // unused ids that have been garbage collected
473 | this.exited = false; // whether the Go program has exited
474 |
475 | const mem = new DataView(this._inst.exports.memory.buffer)
476 |
477 | while (true) {
478 | const callbackPromise = new Promise((resolve) => {
479 | this._resolveCallbackPromise = () => {
480 | if (this.exited) {
481 | throw new Error("bad callback: Go program has already exited");
482 | }
483 | setTimeout(resolve, 0); // make sure it is asynchronous
484 | };
485 | });
486 | this._inst.exports._start();
487 | if (this.exited) {
488 | break;
489 | }
490 | await callbackPromise;
491 | }
492 | }
493 |
494 | _resume() {
495 | if (this.exited) {
496 | throw new Error("Go program has already exited");
497 | }
498 | this._inst.exports.resume();
499 | if (this.exited) {
500 | this._resolveExitPromise();
501 | }
502 | }
503 |
504 | _makeFuncWrapper(id) {
505 | const go = this;
506 | return function () {
507 | const event = { id: id, this: this, args: arguments };
508 | go._pendingEvent = event;
509 | go._resume();
510 | return event.result;
511 | };
512 | }
513 | }
514 |
515 | if (
516 | global.require &&
517 | global.require.main === module &&
518 | global.process &&
519 | global.process.versions &&
520 | !global.process.versions.electron
521 | ) {
522 | if (process.argv.length != 3) {
523 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
524 | process.exit(1);
525 | }
526 |
527 | const go = new Go();
528 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
529 | return go.run(result.instance);
530 | }).catch((err) => {
531 | console.error(err);
532 | process.exit(1);
533 | });
534 | }
535 | })();
536 |
--------------------------------------------------------------------------------
/examples/web/worker_g16.js:
--------------------------------------------------------------------------------
1 | importScripts("wasm_exec.js");
2 |
3 | const workerConsole = {
4 | log: (message) => {
5 | postMessage({ type: "log", message: message });
6 | },
7 | error: (message) => {
8 | postMessage({ type: "error", message: message });
9 | },
10 | };
11 |
12 | console.log = workerConsole.log;
13 | console.error = workerConsole.error;
14 |
15 | const go = new Go();
16 | const WASM_URL = "./artifacts/g16_prover.wasm";
17 |
18 | // Replace the console.log function in the Go environment
19 | go.importObject.env["syscall/js.console_log"] = (sp) => {
20 | const s = go._inst.exports.ram.loadString(sp);
21 | postMessage({ type: "log", message: s });
22 | };
23 |
24 | onmessage = async (event) => {
25 | if (event.data.type === "generateProof") {
26 | const witness = event.data.witness;
27 |
28 | try {
29 | let wasm;
30 |
31 | // Instantiate and run the wasm module with the modified Go environment
32 | if ('instantiateStreaming' in WebAssembly) {
33 | const result = await WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject);
34 | wasm = result.instance;
35 | } else {
36 | const resp = await fetch(WASM_URL);
37 | const bytes = await resp.arrayBuffer();
38 | const result = await WebAssembly.instantiate(bytes, go.importObject);
39 | wasm = result.instance;
40 | }
41 |
42 | go.run(wasm);
43 |
44 | // Call the generateProof function with the witness data
45 | elapsedTime = generateProof(witness);
46 |
47 | // Calculate the time taken for proof generation
48 | console.log(`Benchkmark output time: ${elapsedTime} ms`);
49 |
50 | // Send the result back to the main thread
51 | postMessage({ type: "proofGenerated", result: elapsedTime });
52 | } catch (error) {
53 | postMessage({ type: "error", message: error.message });
54 | }
55 | }
56 | };
--------------------------------------------------------------------------------
/examples/web/worker_plonk.js:
--------------------------------------------------------------------------------
1 | importScripts("wasm_exec.js");
2 |
3 | const workerConsole = {
4 | log: (message) => {
5 | postMessage({ type: "log", message: message });
6 | },
7 | error: (message) => {
8 | postMessage({ type: "error", message: message });
9 | },
10 | };
11 |
12 | console.log = workerConsole.log;
13 | console.error = workerConsole.error;
14 |
15 | const go = new Go();
16 | const WASM_URL = "./artifacts/plonk_prover.wasm";
17 |
18 | // Replace the console.log function in the Go environment
19 | go.importObject.env["syscall/js.console_log"] = (sp) => {
20 | const s = go._inst.exports.ram.loadString(sp);
21 | postMessage({ type: "log", message: s });
22 | };
23 |
24 | onmessage = async (event) => {
25 | if (event.data.type === "generateProof") {
26 | const witness = event.data.witness;
27 |
28 | try {
29 | let wasm;
30 |
31 | // Instantiate and run the wasm module with the modified Go environment
32 | if ('instantiateStreaming' in WebAssembly) {
33 | const result = await WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject);
34 | wasm = result.instance;
35 | } else {
36 | const resp = await fetch(WASM_URL);
37 | const bytes = await resp.arrayBuffer();
38 | const result = await WebAssembly.instantiate(bytes, go.importObject);
39 | wasm = result.instance;
40 | }
41 |
42 | go.run(wasm);
43 |
44 | // Call the generateProof function with the witness data
45 | elapsedTime = generateProof(witness);
46 |
47 | // Calculate the time taken for proof generation
48 | console.log(`Benchkmark output time: ${elapsedTime} ms`);
49 |
50 | // Send the result back to the main thread
51 | postMessage({ type: "proofGenerated", result: elapsedTime });
52 | } catch (error) {
53 | postMessage({ type: "error", message: error.message });
54 | }
55 | }
56 | };
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module gnark-prover-tinygo
2 |
3 | go 1.19
4 |
5 | // replace github.com/vocdoni/gnark-wasm-prover => /home/p4u/repos/vocdoni/gprover
6 | // replace github.com/vocdoni/gnark-wasm-prover => /Users/lucasmenendez/Workspace/vocdoni/forks/gnark-wasm-prover
7 | // replace github.com/vocdoni/gnark-crypto => /home/p4u/repos/vocdoni/gnark-crypto
8 | // replace github.com/consensys/gnark => /home/p4u/repos/vocdoni/gnark
9 |
10 | replace github.com/consensys/gnark => github.com/vocdoni/gnark v0.0.0-20230413134136-187f3b3ead69
11 |
12 | replace github.com/consensys/gnark-crypto => github.com/vocdoni/gnark-crypto v0.10.1-0.20230411213837-3e72368bec7e
13 |
14 | require (
15 | github.com/consensys/gnark v0.7.2-0.20230314134620-02dca8cec284
16 | github.com/consensys/gnark-crypto v0.9.2-0.20230314094804-5185eb8c3978
17 | github.com/frankban/quicktest v1.14.4
18 | github.com/iden3/go-iden3-crypto v0.0.13
19 | go.vocdoni.io/dvote v1.0.4-0.20230321132211-6095f229cd59
20 | )
21 |
22 | require (
23 | github.com/DataDog/zstd v1.4.8 // indirect
24 | github.com/beorn7/perks v1.0.1 // indirect
25 | github.com/bits-and-blooms/bitset v1.5.0 // indirect
26 | github.com/blang/semver/v4 v4.0.0 // indirect
27 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
28 | github.com/cockroachdb/errors v1.8.9 // indirect
29 | github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
30 | github.com/cockroachdb/pebble v0.0.0-20230309163202-51422ae2d449 // indirect
31 | github.com/cockroachdb/redact v1.1.3 // indirect
32 | github.com/consensys/bavard v0.1.13 // indirect
33 | github.com/davecgh/go-spew v1.1.1 // indirect
34 | github.com/getsentry/sentry-go v0.12.0 // indirect
35 | github.com/glendc/go-external-ip v0.1.0 // indirect
36 | github.com/gogo/protobuf v1.3.2 // indirect
37 | github.com/golang/protobuf v1.5.2 // indirect
38 | github.com/golang/snappy v0.0.4 // indirect
39 | github.com/google/go-cmp v0.5.9 // indirect
40 | github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
41 | github.com/klauspost/compress v1.16.0 // indirect
42 | github.com/kr/pretty v0.3.1 // indirect
43 | github.com/kr/text v0.2.0 // indirect
44 | github.com/mattn/go-colorable v0.1.13 // indirect
45 | github.com/mattn/go-isatty v0.0.17 // indirect
46 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
47 | github.com/mmcloughlin/addchain v0.4.0 // indirect
48 | github.com/pkg/errors v0.9.1 // indirect
49 | github.com/pmezard/go-difflib v1.0.0 // indirect
50 | github.com/prometheus/client_golang v1.14.0 // indirect
51 | github.com/prometheus/client_model v0.3.0 // indirect
52 | github.com/prometheus/common v0.37.0 // indirect
53 | github.com/prometheus/procfs v0.9.0 // indirect
54 | github.com/rogpeppe/go-internal v1.9.0 // indirect
55 | github.com/rs/zerolog v1.29.0 // indirect
56 | github.com/stretchr/testify v1.8.2 // indirect
57 | golang.org/x/crypto v0.7.0 // indirect
58 | golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect
59 | golang.org/x/sys v0.6.0 // indirect
60 | google.golang.org/protobuf v1.28.1 // indirect
61 | gopkg.in/yaml.v3 v3.0.1 // indirect
62 | rsc.io/tmplfunc v0.0.3 // indirect
63 | )
64 |
--------------------------------------------------------------------------------
/internal/zkaddress/zkaddress.go:
--------------------------------------------------------------------------------
1 | package zkaddress
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards"
7 | "github.com/iden3/go-iden3-crypto/poseidon"
8 | )
9 |
10 | const DefaultZkAddressLen = 20
11 |
12 | type ZkAddress struct {
13 | Private *big.Int
14 | Public *big.Int
15 | Scalar *big.Int
16 | }
17 |
18 | func (zkAddr *ZkAddress) ArboBytes() []byte {
19 | bScalar := zkAddr.Scalar.Bytes()
20 | // swap endianess
21 | for i, j := 0, len(bScalar)-1; i < j; i, j = i+1, j-1 {
22 | bScalar[i], bScalar[j] = bScalar[j], bScalar[i]
23 | }
24 | // truncate to default length and return
25 | res := make([]byte, DefaultZkAddressLen)
26 | copy(res[:], bScalar)
27 | return res[:]
28 | }
29 |
30 | func FromBytes(seed []byte) (*ZkAddress, error) {
31 | // Setup the curve
32 | c := twistededwards.GetEdwardsCurve()
33 | // Get scalar private key hashing the seed with Poseidon hash
34 | private, err := poseidon.HashBytes(seed)
35 | if err != nil {
36 | return nil, err
37 | }
38 | // Get the point of the curve that represents the public key multipliying
39 | // the private key scalar by the base of the curve
40 | point := new(twistededwards.PointAffine).ScalarMultiplication(&c.Base, private)
41 | // Get the single scalar that represents the publick key hashing X, Y point
42 | // coordenates with Poseidon hash
43 | bX, bY := new(big.Int), new(big.Int)
44 | bX = point.X.BigInt(bX)
45 | bY = point.Y.BigInt(bY)
46 | public, err := poseidon.Hash([]*big.Int{bX, bY})
47 | if err != nil {
48 | return nil, err
49 | }
50 |
51 | // truncate the most significant n bytes of the public key (little endian)
52 | // where n is the default ZkAddress length
53 | publicBytes := public.Bytes()
54 | m := len(publicBytes) - DefaultZkAddressLen
55 | scalar := new(big.Int).SetBytes(publicBytes[m:])
56 | return &ZkAddress{private, public, scalar}, nil
57 | }
58 |
--------------------------------------------------------------------------------
/internal/zkaddress/zkaddress_test.go:
--------------------------------------------------------------------------------
1 | package zkaddress
2 |
3 | import (
4 | "testing"
5 |
6 | qt "github.com/frankban/quicktest"
7 | )
8 |
9 | func TestFromBytes(t *testing.T) {
10 | c := qt.New(t)
11 |
12 | seed := []byte("1b505cdafb4b1150b1a740633af41e5e1f19a5c4")
13 | zkAddr, err := FromBytes(seed)
14 | c.Assert(err, qt.IsNil)
15 | c.Assert(zkAddr.Private.String(), qt.Equals, "12007696602022466067210558438468234995085206818257350359618361229442198701667")
16 | c.Assert(zkAddr.Public.String(), qt.Equals, "19597797733822453932297698102694210740977986979020091017779598307769964166976")
17 | c.Assert(zkAddr.Scalar.String(), qt.Equals, "647903732945896451226807429503635300036365909824")
18 | }
19 |
--------------------------------------------------------------------------------
/prover/groth16.go:
--------------------------------------------------------------------------------
1 | package prover
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/consensys/gnark-crypto/ecc"
9 | "github.com/consensys/gnark/backend/groth16"
10 | "github.com/consensys/gnark/backend/witness"
11 | "github.com/consensys/gnark/std"
12 |
13 | // This import fixes the issue that raises when a prover tries to generate a proof
14 | // of a serialized circuit. Check more information here:
15 | // - https://github.com/ConsenSys/gnark/issues/600
16 | // - https://github.com/phated/gnark-browser/blob/2446c65e89156f1a04163724a89e5dcb7e4c4886/README.md#solution-hint-registration
17 | _ "github.com/consensys/gnark/std/math/bits"
18 | )
19 |
20 | // GenerateProofGroth16 sets up the circuit with the constrain system and the srs files
21 | // provided and generates the proof for the JSON encoded inputs (witness). It
22 | // returns the verification key, the proof and the public witness, all of this
23 | // outputs will be encoded as JSON. If something fails, it returns an error.
24 | func GenerateProofGroth16(bccs, bpkey, inputs []byte) ([]byte, []byte, error) {
25 | step := time.Now()
26 | // Read and initialize circuit CS
27 | ccs := groth16.NewCS(ecc.BN254)
28 | if _, err := ccs.ReadFrom(bytes.NewReader(bccs)); err != nil {
29 | fmt.Println("error reading circuit cs: ", err)
30 | return nil, nil, fmt.Errorf("error reading circuit cs: %w", err)
31 | }
32 | fmt.Println("ccs loaded, took (s):", time.Since(step))
33 | step = time.Now()
34 | // Read proving key
35 | provingKey := groth16.NewProvingKey(ecc.BN254)
36 | if _, err := provingKey.UnsafeReadFrom(bytes.NewReader(bpkey)); err != nil {
37 | fmt.Println("error reading circuit pkey: ", err)
38 | return nil, nil, fmt.Errorf("error reading circuit pkey: %w", err)
39 | }
40 | fmt.Println("pKey loaded, took (s):", time.Since(step))
41 | step = time.Now()
42 | // Read and initialize the witness
43 | cWitness, err := witness.New(ecc.BN254.ScalarField())
44 | if err != nil {
45 | fmt.Println("error initializing witness: ", err)
46 | return nil, nil, fmt.Errorf("error initializing witness: %w", err)
47 | }
48 | if _, err := cWitness.ReadFrom(bytes.NewReader(inputs)); err != nil {
49 | fmt.Println("error reading witness: ", err)
50 | return nil, nil, fmt.Errorf("error reading witness: %w", err)
51 | }
52 | fmt.Println("witness loaded, took (s):", time.Since(step))
53 |
54 | std.RegisterHints()
55 |
56 | step = time.Now()
57 | // Generate the proof
58 | proof, err := groth16.Prove(ccs, provingKey, cWitness)
59 | if err != nil {
60 | fmt.Printf("error generating proof: %v\n", err)
61 | return nil, nil, fmt.Errorf("error generating proof: %w", err)
62 | }
63 | fmt.Println("proof generated, took (s):", time.Since(step))
64 | proofBuff := bytes.Buffer{}
65 | if _, err := proof.WriteTo(&proofBuff); err != nil {
66 | return nil, nil, fmt.Errorf("error encoding proof: %w", err)
67 | }
68 | // Get public witness part and encode it
69 | publicWitness, err := cWitness.Public()
70 | if err != nil {
71 | return nil, nil, fmt.Errorf("error generating public witness: %w", err)
72 | }
73 | publicWitnessBuff := bytes.Buffer{}
74 | if _, err := publicWitness.WriteTo(&publicWitnessBuff); err != nil {
75 | return nil, nil, fmt.Errorf("error encoding public witness: %w", err)
76 | }
77 | return proofBuff.Bytes(), publicWitnessBuff.Bytes(), nil
78 | }
79 |
--------------------------------------------------------------------------------
/prover/prover.go:
--------------------------------------------------------------------------------
1 | package prover
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/consensys/gnark-crypto/ecc"
9 | "github.com/consensys/gnark-crypto/kzg"
10 | prover "github.com/consensys/gnark/backend/plonk/bn254"
11 | witness "github.com/consensys/gnark/backend/witness"
12 | csbn254 "github.com/consensys/gnark/constraint/bn254"
13 | "github.com/consensys/gnark/std"
14 | )
15 |
16 | // GenerateProofPlonk sets up the circuit with the constrain system and the srs files
17 | // provided and generates the proof for the JSON encoded inputs (witness). It
18 | // returns the verification key, the proof and the public witness, all of this
19 | // outputs will be encoded as JSON. If something fails, it returns an error.
20 | func GenerateProofPlonk(bccs, bsrs, bpkey, inputs []byte) ([]byte, []byte, error) {
21 | step := time.Now()
22 | // Read and initialize circuit CS
23 | fmt.Println("loading circuit...")
24 | ccs := &csbn254.SparseR1CS{}
25 | if _, err := ccs.ReadFrom(bytes.NewReader(bccs)); err != nil {
26 | return nil, nil, fmt.Errorf("error reading circuit cs: %w", err)
27 | }
28 | fmt.Println("ccs loaded, took (s):", time.Since(step))
29 | step = time.Now()
30 | // Read and initialize SSR
31 | srs := kzg.NewSRS(ecc.BN254)
32 | if _, err := srs.ReadFrom(bytes.NewReader(bsrs)); err != nil {
33 | return nil, nil, err
34 | }
35 | fmt.Println("srs loaded, took (s):", time.Since(step))
36 | step = time.Now()
37 | // Read proving key
38 | provingKey := &prover.ProvingKey{}
39 | if _, err := provingKey.ReadFrom(bytes.NewReader(bpkey)); err != nil {
40 | return nil, nil, fmt.Errorf("error reading circuit pkey: %w", err)
41 | }
42 | fmt.Println("pKey loaded, took (s):", time.Since(step))
43 | step = time.Now()
44 | // Instance KZG into the proving key
45 | if err := provingKey.InitKZG(srs); err != nil {
46 | return nil, nil, fmt.Errorf("error initializating kzg into the pkey: %w", err)
47 | }
48 | fmt.Println("kzg initializated into the pKey, took (s):", time.Since(step))
49 | step = time.Now()
50 | // Read and initialize the witness
51 | cWitness, err := witness.New(ecc.BN254.ScalarField())
52 | if err != nil {
53 | return nil, nil, fmt.Errorf("error initializing witness: %w", err)
54 | }
55 | if _, err := cWitness.ReadFrom(bytes.NewReader(inputs)); err != nil {
56 | return nil, nil, fmt.Errorf("error reading witness: %w", err)
57 | }
58 | fmt.Println("witness loaded, took (s):", time.Since(step))
59 | step = time.Now()
60 |
61 | // Initialize the hints registry
62 | std.RegisterHints()
63 |
64 | // Generate the proof
65 | fmt.Println("generating proof...")
66 | proof, err := prover.Prove(ccs, provingKey, cWitness)
67 | if err != nil {
68 | fmt.Println("error generating proof:", err.Error())
69 | return nil, nil, fmt.Errorf("error generating proof: %w", err)
70 | }
71 | fmt.Println("proof generated, took (s):", time.Since(step))
72 | proofBuff := bytes.Buffer{}
73 | if _, err := proof.WriteTo(&proofBuff); err != nil {
74 | return nil, nil, fmt.Errorf("error encoding proof: %w", err)
75 | }
76 | pKeyBuff := bytes.Buffer{}
77 | if _, err := provingKey.WriteTo(&pKeyBuff); err != nil {
78 | return nil, nil, fmt.Errorf("error encoding proof: %w", err)
79 | }
80 | // Get public witness part and encode it
81 | publicWitness, err := cWitness.Public()
82 | if err != nil {
83 | return nil, nil, fmt.Errorf("error generating public witness: %w", err)
84 | }
85 | publicWitnessBuff := bytes.Buffer{}
86 | if _, err := publicWitness.WriteTo(&publicWitnessBuff); err != nil {
87 | return nil, nil, fmt.Errorf("error encoding public witness: %w", err)
88 | }
89 | return proofBuff.Bytes(), publicWitnessBuff.Bytes(), nil
90 | }
91 |
--------------------------------------------------------------------------------
/prover/prover_test.go:
--------------------------------------------------------------------------------
1 | package prover
2 |
3 | import (
4 | "bytes"
5 | "crypto/rand"
6 | "testing"
7 |
8 | "github.com/consensys/gnark-crypto/ecc"
9 | "github.com/consensys/gnark-crypto/ecc/bn254/fr/kzg"
10 | "github.com/consensys/gnark/constraint"
11 |
12 | plonk "github.com/consensys/gnark/backend/plonk/bn254"
13 | cs "github.com/consensys/gnark/constraint/bn254"
14 |
15 | "github.com/consensys/gnark/frontend"
16 | "github.com/consensys/gnark/frontend/cs/scs"
17 |
18 | qt "github.com/frankban/quicktest"
19 | )
20 |
21 | type testingCircuit struct {
22 | A, B frontend.Variable `gnark:",public"`
23 | Result frontend.Variable
24 | }
25 |
26 | func (c *testingCircuit) Define(api frontend.API) error {
27 | api.AssertIsEqual(api.Mul(c.A, c.B), c.Result)
28 | return nil
29 | }
30 |
31 | func newKZGSRS(ccs constraint.ConstraintSystem) (*kzg.SRS, error) {
32 | nbConstraints := ccs.GetNbConstraints()
33 | sizeSystem := nbConstraints + ccs.GetNbPublicVariables()
34 | kzgSize := ecc.NextPowerOfTwo(uint64(sizeSystem)) + 3
35 |
36 | curveID := ecc.BN254
37 | alpha, err := rand.Int(rand.Reader, curveID.ScalarField())
38 | if err != nil {
39 | return nil, err
40 | }
41 | return kzg.NewSRS(kzgSize, alpha)
42 | }
43 |
44 | func TestEnd2EndCircuit(t *testing.T) {
45 | c := qt.New(t)
46 |
47 | var circuit testingCircuit
48 | ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit)
49 | c.Assert(err, qt.IsNil)
50 |
51 | var ccsBuff bytes.Buffer
52 | _, err = ccs.WriteTo(&ccsBuff)
53 | c.Assert(err, qt.IsNil)
54 |
55 | srs, err := newKZGSRS(ccs)
56 | c.Assert(err, qt.IsNil)
57 | var srsBuff bytes.Buffer
58 | _, err = srs.WriteTo(&srsBuff)
59 | c.Assert(err, qt.IsNil)
60 |
61 | _scs := ccs.(*cs.SparseR1CS)
62 | provingKey, _, err := plonk.Setup(_scs, srs)
63 | c.Assert(err, qt.IsNil)
64 | var pkeyBuff bytes.Buffer
65 | _, err = provingKey.WriteTo(&pkeyBuff)
66 | c.Assert(err, qt.IsNil)
67 |
68 | inputs := &testingCircuit{
69 | A: 2,
70 | B: 3,
71 | Result: 6,
72 | }
73 | wtns, err := frontend.NewWitness(inputs, ecc.BN254.ScalarField())
74 | c.Assert(err, qt.IsNil)
75 |
76 | wtnsBuff := bytes.Buffer{}
77 | _, err = wtns.WriteTo(&wtnsBuff)
78 | c.Assert(err, qt.IsNil)
79 |
80 | _, _, err = GenerateProofPlonk(ccsBuff.Bytes(), srsBuff.Bytes(), pkeyBuff.Bytes(), wtnsBuff.Bytes())
81 | c.Assert(err, qt.IsNil)
82 | }
83 |
--------------------------------------------------------------------------------
/std/hash/poseidon/poseidon.go:
--------------------------------------------------------------------------------
1 | package poseidon
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/consensys/gnark/frontend"
7 | )
8 |
9 | type Poseidon struct {
10 | api frontend.API
11 | data []frontend.Variable
12 | }
13 |
14 | func Hash(api frontend.API, inputs ...frontend.Variable) frontend.Variable {
15 | h := NewPoseidon(api)
16 | h.Write(inputs...)
17 | return h.Sum()
18 | }
19 |
20 | func NewPoseidon(api frontend.API) Poseidon {
21 | return Poseidon{
22 | api: api,
23 | data: []frontend.Variable{},
24 | }
25 | }
26 |
27 | func (h *Poseidon) Write(data ...frontend.Variable) {
28 | h.data = append(h.data, data...)
29 | }
30 |
31 | func (h *Poseidon) Reset() {
32 | h.data = []frontend.Variable{}
33 | }
34 |
35 | func (h *Poseidon) Sum() frontend.Variable {
36 | nInputs := len(h.data)
37 | // Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
38 | // Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
39 | // And rounded up to nearest integer that divides by t
40 | nRoundsPC := [16]int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68}
41 | t := nInputs + 1
42 | nRoundsF := 8
43 | nRoundsP := nRoundsPC[t-2]
44 | c := getConstant(C, t)
45 | s := getConstant(S, t)
46 | m := getConstant(M, t)
47 | p := getConstant(P, t)
48 |
49 | state := make([]frontend.Variable, t)
50 | for j := 0; j < t; j++ {
51 | if j == 0 {
52 | state[0] = 0
53 | } else {
54 | state[j] = h.data[j-1]
55 | }
56 | }
57 | state = h.ark(state, c, 0)
58 |
59 | for r := 0; r < nRoundsF/2-1; r++ {
60 | for j := 0; j < t; j++ {
61 | state[j] = h.sigma(state[j])
62 | }
63 | state = h.ark(state, c, (r+1)*t)
64 | state = h.mix(state, m)
65 | }
66 |
67 | for j := 0; j < t; j++ {
68 | state[j] = h.sigma(state[j])
69 | }
70 | state = h.ark(state, c, nRoundsF/2*t)
71 | state = h.mix(state, p)
72 |
73 | for r := 0; r < nRoundsP; r++ {
74 |
75 | state[0] = h.sigma(state[0])
76 |
77 | state[0] = h.api.Add(state[0], c[(nRoundsF/2+1)*t+r])
78 | newState0 := frontend.Variable(0)
79 | for j := 0; j < len(state); j++ {
80 | mul := h.api.Mul(s[(t*2-1)*r+j], state[j])
81 | newState0 = h.api.Add(newState0, mul)
82 | }
83 |
84 | for k := 1; k < t; k++ {
85 | state[k] = h.api.Add(state[k], h.api.Mul(state[0], s[(t*2-1)*r+t+k-1]))
86 | }
87 | state[0] = newState0
88 | }
89 |
90 | for r := 0; r < nRoundsF/2-1; r++ {
91 | for j := 0; j < t; j++ {
92 | state[j] = h.sigma(state[j])
93 | }
94 | state = h.ark(state, c, (nRoundsF/2+1)*t+nRoundsP+r*t)
95 | state = h.mix(state, m)
96 | }
97 |
98 | for j := 0; j < t; j++ {
99 | state[j] = h.sigma(state[j])
100 | }
101 |
102 | out := h.mixLast(state, m, 0)
103 | h.data = []frontend.Variable{}
104 | return out
105 | }
106 |
107 | func (h *Poseidon) sigma(in frontend.Variable) frontend.Variable {
108 | in2 := h.api.Mul(in, in)
109 | in4 := h.api.Mul(in2, in2)
110 | return h.api.Mul(in4, in)
111 | }
112 |
113 | func (h *Poseidon) ark(in []frontend.Variable, c []*big.Int, r int) []frontend.Variable {
114 | out := make([]frontend.Variable, len(in))
115 | for i, v := range in {
116 | out[i] = h.api.Add(v, c[i+r])
117 | }
118 | return out
119 | }
120 |
121 | func (h *Poseidon) mix(in []frontend.Variable, m [][]*big.Int) []frontend.Variable {
122 | t := len(in)
123 | out := make([]frontend.Variable, t)
124 | for i := 0; i < t; i++ {
125 | lc := frontend.Variable(0)
126 | for j := 0; j < t; j++ {
127 | lc = h.api.Add(lc, h.api.Mul(m[j][i], in[j]))
128 | }
129 | out[i] = lc
130 | }
131 | return out
132 | }
133 |
134 | func (h *Poseidon) mixLast(in []frontend.Variable, m [][]*big.Int, s int) frontend.Variable {
135 | t := len(in)
136 | out := frontend.Variable(0)
137 | for j := 0; j < t; j++ {
138 | out = h.api.Add(out, h.api.Mul(m[j][s], in[j]))
139 | }
140 | return out
141 | }
142 |
--------------------------------------------------------------------------------
/std/hash/poseidon/poseidon_test.go:
--------------------------------------------------------------------------------
1 | package poseidon
2 |
3 | import (
4 | "math/big"
5 | "testing"
6 |
7 | "github.com/consensys/gnark-crypto/ecc"
8 | "github.com/consensys/gnark/backend"
9 | "github.com/consensys/gnark/frontend"
10 | "github.com/consensys/gnark/test"
11 | hash "github.com/iden3/go-iden3-crypto/poseidon"
12 | )
13 |
14 | type testPoseidonCiruit struct {
15 | Data frontend.Variable
16 | Hash frontend.Variable `gnark:",public"`
17 | }
18 |
19 | func (circuit *testPoseidonCiruit) Define(api frontend.API) error {
20 | h := Hash(api, circuit.Data)
21 | api.AssertIsEqual(h, circuit.Hash)
22 | return nil
23 | }
24 |
25 | func TestPoseidon(t *testing.T) {
26 | assert := test.NewAssert(t)
27 |
28 | var circuit, assignment testPoseidonCiruit
29 |
30 | input, _ := new(big.Int).SetString("297262668938251460872476410954775437897592223497", 10)
31 | assignment.Data = input
32 | assignment.Hash, _ = hash.Hash([]*big.Int{input})
33 |
34 | assert.SolvingSucceeded(&circuit, &assignment, test.WithCurves(ecc.BN254), test.WithBackends(backend.PLONK))
35 | }
36 |
--------------------------------------------------------------------------------
/std/smt/utils.go:
--------------------------------------------------------------------------------
1 | package smt
2 |
3 | import (
4 | "gnark-prover-tinygo/std/hash/poseidon"
5 |
6 | "github.com/consensys/gnark/frontend"
7 | )
8 |
9 | // endLeafValue returns the encoded childless leaf value for the key-value pair
10 | // provided, hashing it with the predefined hashing function 'H':
11 | //
12 | // newLeafValue = H(key | value | 1)
13 | func endLeafValue(api frontend.API, key, value frontend.Variable) frontend.Variable {
14 | return poseidon.Hash(api, key, value, 1)
15 | }
16 |
17 | // intermediateLeafValue returns the encoded intermediate leaf value for the
18 | // key-value pair provided, hashing it with the predefined hashing function 'H':
19 | //
20 | // intermediateLeafValue = H(l | r)
21 | func intermediateLeafValue(api frontend.API, l, r frontend.Variable) frontend.Variable {
22 | return poseidon.Hash(api, l, r)
23 | }
24 |
25 | func switcher(api frontend.API, sel, l, r frontend.Variable) (outL, outR frontend.Variable) {
26 | // aux <== (R-L)*sel;
27 | aux := api.Mul(api.Sub(r, l), sel)
28 | // outL <== aux + L;
29 | outL = api.Add(aux, l)
30 | // outR <== -aux + R;
31 | outR = api.Sub(r, aux)
32 | return
33 | }
34 |
35 | func multiAnd(api frontend.API, inputs ...frontend.Variable) frontend.Variable {
36 | if len(inputs) == 0 {
37 | return 0
38 | }
39 | if len(inputs) == 1 {
40 | return inputs[0]
41 | }
42 |
43 | res := inputs[0]
44 | for i := 1; i < len(inputs); i++ {
45 | res = api.And(res, inputs[i])
46 | }
47 | return res
48 | }
49 |
--------------------------------------------------------------------------------
/std/smt/verifier.go:
--------------------------------------------------------------------------------
1 | // smt is a port of Circom SMTVerifier. It attempts to check a proof
2 | // of a Sparse Merkle Tree (compatible with Arbo Merkle Tree implementation).
3 | // Check the original implementation from Iden3:
4 | // - https://github.com/iden3/circomlib/tree/a8cdb6cd1ad652cca1a409da053ec98f19de6c9d/circuits/smt
5 | package smt
6 |
7 | import "github.com/consensys/gnark/frontend"
8 |
9 | func Verifier(api frontend.API,
10 | root, key, value frontend.Variable, siblings []frontend.Variable) error {
11 | return smtverifier(api, 1, root, 0, 0, 0, key, value, 0, siblings)
12 | }
13 |
14 | func smtverifier(api frontend.API,
15 | enabled, root, oldKey, oldValue, isOld0, key, value, fnc frontend.Variable,
16 | siblings []frontend.Variable) error {
17 | nLevels := len(siblings)
18 |
19 | // Steps:
20 | // 1. Get the hash of both key-value pairs, old and new one.
21 | // 2. Get the binary representation of the key new.
22 | // 3. Get the path of the current key.
23 | // 4. Calculate the root with the siblings provided.
24 | // 5. Compare the calculated root with the provided one.
25 |
26 | // [STEP 1]
27 | // hash1Old = H(oldKey | oldValue | 1)
28 | hash1Old := endLeafValue(api, oldKey, oldValue)
29 | // hash1New = H(key | value | 1)
30 | hash1New := endLeafValue(api, key, value)
31 |
32 | // [STEP 2]
33 | // component n2bNew = Num2Bits_strict();
34 | // n2bNew.in <== key;
35 | n2bNew := api.ToBinary(key, api.Compiler().FieldBitLen())
36 |
37 | // [STEP 3]
38 | // component smtLevIns = SMTLevIns(nLevels);
39 | // for (i=0; i 0; i-- {
145 | // levIns[i] = (1-isDone[i])*(1-isZero[i-1])
146 | levIns[i] = api.Mul(api.Sub(1, isDone[i]), api.Sub(1, isZero[i-1]))
147 | // done[i-1] = levIns[i] + done[i]
148 | isDone[i-1] = api.Add(levIns[i], isDone[i])
149 | }
150 | // levIns[0] <== (1-done[0]);
151 | levIns[0] = api.Sub(1, isDone[0])
152 |
153 | return levIns
154 | }
155 |
156 | func smtVerifierSM(api frontend.API,
157 | is0, levIns, fnc, prevTop, prevI0, prevIold, prevInew, prevNa frontend.Variable) (
158 | stTop, stIold, stI0, stInew, stNa frontend.Variable) {
159 | // prev_top_lev_ins <== prev_top * levIns;
160 | prevTopLevIns := api.Mul(prevTop, levIns)
161 | // prev_top_lev_ins_fnc <== prev_top_lev_ins*fnc
162 | prevTopLevInsFnc := api.Mul(prevTopLevIns, fnc)
163 |
164 | stTop = api.Sub(prevTop, prevTopLevIns) // st_top <== prev_top - prev_top_lev_ins
165 | stIold = api.Mul(prevTopLevInsFnc, api.Sub(1, is0)) // st_iold <== prev_top_lev_ins_fnc * (1 - is0)
166 | stI0 = api.Mul(prevTopLevIns, is0) // st_i0 <== prev_top_lev_ins * is0;
167 | stInew = api.Sub(prevTopLevIns, prevTopLevInsFnc) // st_inew <== prev_top_lev_ins - prev_top_lev_ins_fnc
168 | stNa = api.Add(prevNa, prevInew, prevIold, prevI0) // st_na <== prev_na + prev_inew + prev_iold + prev_i0
169 | return
170 | }
171 |
172 | func smtVerifierLevel(api frontend.API, stTop, stIold, stInew, sibling,
173 | old1leaf, new1leaf, lrbit, child frontend.Variable) frontend.Variable {
174 | // component switcher = Switcher();
175 | // switcher.sel <== lrbit;
176 | // switcher.L <== child;
177 | // switcher.R <== sibling;
178 | l, r := switcher(api, lrbit, child, sibling)
179 | // component proofHash = SMTHash2();
180 | // proofHash.L <== switcher.outL;
181 | // proofHash.R <== switcher.outR;
182 | proofHash := intermediateLeafValue(api, l, r)
183 | // aux[0] <== proofHash.out * st_top;
184 | aux0 := api.Mul(proofHash, stTop)
185 | // aux[1] <== old1leaf * st_iold;
186 | aux1 := api.Mul(old1leaf, stIold)
187 | // root <== aux[0] + aux[1] + new1leaf * st_inew;
188 | return api.Add(aux0, aux1, api.Mul(new1leaf, stInew))
189 | }
190 |
--------------------------------------------------------------------------------
/std/smt/verifier_test.go:
--------------------------------------------------------------------------------
1 | package smt
2 |
3 | import (
4 | "gnark-prover-tinygo/internal/zkaddress"
5 | "math/big"
6 | "testing"
7 |
8 | "github.com/consensys/gnark-crypto/ecc"
9 | "github.com/consensys/gnark/backend"
10 | "github.com/consensys/gnark/frontend"
11 | "github.com/consensys/gnark/test"
12 | qt "github.com/frankban/quicktest"
13 | "go.vocdoni.io/dvote/db"
14 | "go.vocdoni.io/dvote/db/pebbledb"
15 | "go.vocdoni.io/dvote/tree/arbo"
16 | "go.vocdoni.io/dvote/util"
17 | )
18 |
19 | type testVerifierCircuit struct {
20 | Root frontend.Variable
21 | Key frontend.Variable
22 | Value frontend.Variable
23 | Siblings [160]frontend.Variable
24 | }
25 |
26 | func (circuit *testVerifierCircuit) Define(api frontend.API) error {
27 | return Verifier(api, circuit.Root, circuit.Key, circuit.Value, circuit.Siblings[:])
28 | }
29 |
30 | func successInputs(t *testing.T) testVerifierCircuit {
31 | c := qt.New(t)
32 |
33 | database, err := pebbledb.New(db.Options{Path: t.TempDir()})
34 | c.Assert(err, qt.IsNil)
35 | arboTree, err := arbo.NewTree(arbo.Config{
36 | Database: database,
37 | MaxLevels: 160,
38 | HashFunction: arbo.HashFunctionPoseidon,
39 | })
40 | c.Assert(err, qt.IsNil)
41 |
42 | factoryWeight := big.NewInt(10)
43 | candidate, err := zkaddress.FromBytes(util.RandomBytes(32))
44 | c.Assert(err, qt.IsNil)
45 | err = arboTree.Add(candidate.ArboBytes(), arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), factoryWeight))
46 | c.Assert(err, qt.IsNil)
47 |
48 | for i := 0; i < 100; i++ {
49 | k, err := zkaddress.FromBytes(util.RandomBytes(32))
50 | c.Assert(err, qt.IsNil)
51 | err = arboTree.Add(k.ArboBytes(), arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), factoryWeight))
52 | c.Assert(err, qt.IsNil)
53 | }
54 |
55 | key, value, pSiblings, exist, err := arboTree.GenProof(candidate.ArboBytes())
56 | c.Assert(err, qt.IsNil)
57 | c.Assert(exist, qt.IsTrue)
58 | c.Assert(key, qt.ContentEquals, candidate.ArboBytes())
59 | c.Assert(value, qt.ContentEquals, arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), factoryWeight))
60 |
61 | uSiblings, err := arbo.UnpackSiblings(arbo.HashFunctionPoseidon, pSiblings)
62 | c.Assert(err, qt.IsNil)
63 |
64 | siblings := [160]frontend.Variable{}
65 | for i := 0; i < 160; i++ {
66 | if i < len(uSiblings) {
67 | siblings[i] = arbo.BytesToBigInt(uSiblings[i])
68 | } else {
69 | siblings[i] = big.NewInt(0)
70 | }
71 | }
72 |
73 | root, err := arboTree.Root()
74 | c.Assert(err, qt.IsNil)
75 | return testVerifierCircuit{
76 | Root: arbo.BytesToBigInt(root),
77 | Key: candidate.Scalar,
78 | Value: factoryWeight,
79 | Siblings: siblings,
80 | }
81 | }
82 |
83 | func TestVerifier(t *testing.T) {
84 | assert := test.NewAssert(t)
85 |
86 | var circuit testVerifierCircuit
87 | inputs := successInputs(t)
88 | assert.SolvingSucceeded(&circuit, &inputs, test.WithCurves(ecc.BN254), test.WithBackends(backend.PLONK))
89 | }
90 |
--------------------------------------------------------------------------------
/std/zkaddress/zkaddress.go:
--------------------------------------------------------------------------------
1 | package zkaddress
2 |
3 | import (
4 | "gnark-prover-tinygo/std/hash/poseidon"
5 |
6 | ecc "github.com/consensys/gnark-crypto/ecc/twistededwards"
7 | "github.com/consensys/gnark/frontend"
8 | "github.com/consensys/gnark/std/algebra/native/twistededwards"
9 | )
10 |
11 | const DefaultZkAddressLen = 20
12 |
13 | type ZkAddress struct {
14 | Private frontend.Variable
15 | Public frontend.Variable
16 | Scalar frontend.Variable
17 | }
18 |
19 | func FromPrivate(api frontend.API, private frontend.Variable) (ZkAddress, error) {
20 | curve, err := twistededwards.NewEdCurve(api, ecc.BN254)
21 | if err != nil {
22 | return ZkAddress{}, err
23 | }
24 |
25 | base := twistededwards.Point{
26 | X: curve.Params().Base[0],
27 | Y: curve.Params().Base[1],
28 | }
29 | point := curve.ScalarMul(base, private)
30 | public := poseidon.Hash(api, point.X, point.Y)
31 |
32 | bPublic := api.ToBinary(public, api.Compiler().FieldBitLen())
33 | scalar := api.FromBinary(bPublic[:DefaultZkAddressLen*8]...)
34 |
35 | return ZkAddress{
36 | Private: private,
37 | Public: public,
38 | Scalar: scalar,
39 | }, nil
40 | }
41 |
--------------------------------------------------------------------------------
/std/zkaddress/zkaddress_test.go:
--------------------------------------------------------------------------------
1 | package zkaddress
2 |
3 | import (
4 | "gnark-prover-tinygo/internal/zkaddress"
5 | "testing"
6 |
7 | "github.com/consensys/gnark-crypto/ecc"
8 | "github.com/consensys/gnark/backend"
9 | "github.com/consensys/gnark/frontend"
10 | "github.com/consensys/gnark/test"
11 | )
12 |
13 | type testZkAddressCiruit struct {
14 | PrivateKey frontend.Variable
15 | PublicKey frontend.Variable `gnark:",public"`
16 | Scalar frontend.Variable `gnark:",public"`
17 | }
18 |
19 | func (circuit *testZkAddressCiruit) Define(api frontend.API) error {
20 | zkAddress, err := FromPrivate(api, circuit.PrivateKey)
21 | if err != nil {
22 | return err
23 | }
24 |
25 | api.AssertIsEqual(circuit.PrivateKey, zkAddress.Private)
26 | api.AssertIsEqual(circuit.PublicKey, zkAddress.Public)
27 | api.AssertIsEqual(circuit.Scalar, zkAddress.Scalar)
28 | return nil
29 | }
30 |
31 | func TestZkAddress(t *testing.T) {
32 | assert := test.NewAssert(t)
33 |
34 | seed := []byte("1b505cdafb4b1150b1a740633af41e5e1f19a5c4")
35 | zkAddr, err := zkaddress.FromBytes(seed)
36 | if err != nil {
37 | t.Error(err)
38 | }
39 |
40 | assignment := testZkAddressCiruit{
41 | PublicKey: zkAddr.Public,
42 | PrivateKey: zkAddr.Private,
43 | Scalar: zkAddr.Scalar,
44 | }
45 |
46 | var circuit testZkAddressCiruit
47 | assert.SolvingSucceeded(&circuit, &assignment, test.WithCurves(ecc.BN254), test.WithBackends(backend.PLONK))
48 | }
49 |
--------------------------------------------------------------------------------
/wasi/main.go:
--------------------------------------------------------------------------------
1 | //go:build tinygo
2 | // +build tinygo
3 |
4 | package main
5 |
6 | import (
7 | _ "embed"
8 | "fmt"
9 | "gnark-prover-tinygo/prover"
10 | )
11 |
12 | //go:embed zkcensus.ccs
13 | var circuit []byte
14 |
15 | //go:embed zkcensus.pkey
16 | var epkey []byte
17 |
18 | func main() {
19 | c := make(chan interface{})
20 | <-c
21 | }
22 |
23 | //export generateProof
24 | func GenerateProof(bwitness []byte) interface{} {
25 | if _, _, err := prover.GenerateProofGroth16(circuit, epkey, bwitness); err != nil {
26 | fmt.Println(err)
27 | return err.Error()
28 | }
29 | return nil
30 | }
31 |
--------------------------------------------------------------------------------
/wasm/g16/main.go:
--------------------------------------------------------------------------------
1 | //go:build tinygo
2 | // +build tinygo
3 |
4 | package main
5 |
6 | import (
7 | _ "embed"
8 | "fmt"
9 | "gnark-prover-tinygo/prover"
10 | "syscall/js"
11 | "time"
12 | )
13 |
14 | //go:embed zkcensus.ccs
15 | var circuit []byte
16 |
17 | //go:embed zkcensus.pkey
18 | var epkey []byte
19 |
20 | func main() {
21 | c := make(chan int)
22 | js.Global().Set("generateProof", js.FuncOf(jsGenerateProof))
23 | <-c
24 | }
25 |
26 | func jsGenerateProof(this js.Value, args []js.Value) interface{} {
27 | bwitness := make([]byte, args[0].Get("length").Int())
28 | js.CopyBytesToGo(bwitness, args[0])
29 | fmt.Println("Calling function GenerateProof")
30 | startTime := time.Now()
31 | if _, _, err := prover.GenerateProofGroth16(circuit, epkey, bwitness); err != nil {
32 | fmt.Println("Error calling function GenerateProof", err.Error())
33 | return 0
34 | }
35 | elapsedTime := int(time.Now().Sub(startTime).Seconds() * 1000)
36 | return elapsedTime
37 | }
38 |
--------------------------------------------------------------------------------
/wasm/plonk/main.go:
--------------------------------------------------------------------------------
1 | //go:build tinygo
2 | // +build tinygo
3 |
4 | package main
5 |
6 | import (
7 | _ "embed"
8 | "fmt"
9 | "gnark-prover-tinygo/prover"
10 | "syscall/js"
11 | "time"
12 | )
13 |
14 | //go:embed zkcensus.ccs
15 | var eccs []byte
16 |
17 | //go:embed zkcensus.srs
18 | var esrs []byte
19 |
20 | //go:embed zkcensus.pkey
21 | var epkey []byte
22 |
23 | func main() {
24 | c := make(chan int)
25 | js.Global().Set("generateProof", js.FuncOf(jsGenerateProof))
26 | <-c
27 | }
28 |
29 | func jsGenerateProof(this js.Value, args []js.Value) interface{} {
30 | bwitness := make([]byte, args[0].Get("length").Int())
31 | js.CopyBytesToGo(bwitness, args[0])
32 | fmt.Println("Calling function GenerateProof")
33 | startTime := time.Now()
34 | if _, _, err := prover.GenerateProofPlonk(eccs, esrs, epkey, bwitness); err != nil {
35 | fmt.Println("Error calling function GenerateProof", err.Error())
36 | return 0
37 | }
38 | elapsedTime := int(time.Now().Sub(startTime).Seconds() * 1000)
39 | return elapsedTime
40 | }
41 |
--------------------------------------------------------------------------------