@auditable/privacy-pool-zk-sdk 0.0.2-rc.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/cli.js ADDED
@@ -0,0 +1,701 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var fs = require('fs');
5
+ var wasmBindings = require('privacy-pool-client-sdk/pkg/client_sdk_wasm.js');
6
+ var snarkjs = require('snarkjs');
7
+
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
27
+ var wasmBindings__namespace = /*#__PURE__*/_interopNamespaceDefault(wasmBindings);
28
+ var snarkjs__namespace = /*#__PURE__*/_interopNamespaceDefault(snarkjs);
29
+
30
+ const isNode$2 = typeof process !== 'undefined' && !!process.versions?.node;
31
+ let wasmModule = null;
32
+ async function loadWasm(wasmBinary) {
33
+ if (wasmModule)
34
+ return wasmModule;
35
+ if (isNode$2) {
36
+ if (wasmBinary) {
37
+ wasmBindings__namespace.initSync(wasmBinary);
38
+ }
39
+ else {
40
+ const path = await import('path');
41
+ const { fileURLToPath } = await import('url');
42
+ const fs = await import('fs');
43
+ const dir = path.dirname(fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href))));
44
+ const wasmPath = path.resolve(dir, '..', 'pkg', 'client_sdk_wasm_bg.wasm');
45
+ const buffer = fs.readFileSync(wasmPath);
46
+ wasmBindings__namespace.initSync(buffer);
47
+ }
48
+ wasmModule = wasmBindings__namespace;
49
+ }
50
+ else {
51
+ if (wasmBinary) {
52
+ wasmBindings__namespace.initSync(wasmBinary);
53
+ }
54
+ else {
55
+ await wasmBindings__namespace.default();
56
+ }
57
+ wasmModule = wasmBindings__namespace;
58
+ }
59
+ return wasmModule;
60
+ }
61
+
62
+ function getDefaultExportFromCjs (x) {
63
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
64
+ }
65
+
66
+ var witness_calculator = async function builder(code, options) {
67
+
68
+ options = options || {};
69
+
70
+ let wasmModule;
71
+ try {
72
+ wasmModule = await WebAssembly.compile(code);
73
+ } catch (err) {
74
+ console.log(err);
75
+ console.log("\nTry to run circom --c in order to generate c++ code instead\n");
76
+ throw new Error(err);
77
+ }
78
+
79
+ let wc;
80
+
81
+ let errStr = "";
82
+ let msgStr = "";
83
+
84
+ const instance = await WebAssembly.instantiate(wasmModule, {
85
+ runtime: {
86
+ exceptionHandler : function(code) {
87
+ let err;
88
+ if (code == 1) {
89
+ err = "Signal not found.\n";
90
+ } else if (code == 2) {
91
+ err = "Too many signals set.\n";
92
+ } else if (code == 3) {
93
+ err = "Signal already set.\n";
94
+ } else if (code == 4) {
95
+ err = "Assert Failed.\n";
96
+ } else if (code == 5) {
97
+ err = "Not enough memory.\n";
98
+ } else if (code == 6) {
99
+ err = "Input signal array access exceeds the size.\n";
100
+ } else {
101
+ err = "Unknown error.\n";
102
+ }
103
+ throw new Error(err + errStr);
104
+ },
105
+ printErrorMessage : function() {
106
+ errStr += getMessage() + "\n";
107
+ // console.error(getMessage());
108
+ },
109
+ writeBufferMessage : function() {
110
+ const msg = getMessage();
111
+ // Any calls to `log()` will always end with a `\n`, so that's when we print and reset
112
+ if (msg === "\n") {
113
+ console.log(msgStr);
114
+ msgStr = "";
115
+ } else {
116
+ // If we've buffered other content, put a space in between the items
117
+ if (msgStr !== "") {
118
+ msgStr += " ";
119
+ }
120
+ // Then append the message to the message we are creating
121
+ msgStr += msg;
122
+ }
123
+ },
124
+ showSharedRWMemory : function() {
125
+ printSharedRWMemory ();
126
+ }
127
+
128
+ }
129
+ });
130
+
131
+ const sanityCheck =
132
+ options;
133
+ // options &&
134
+ // (
135
+ // options.sanityCheck ||
136
+ // options.logGetSignal ||
137
+ // options.logSetSignal ||
138
+ // options.logStartComponent ||
139
+ // options.logFinishComponent
140
+ // );
141
+
142
+
143
+ wc = new WitnessCalculator(instance, sanityCheck);
144
+ return wc;
145
+
146
+ function getMessage() {
147
+ var message = "";
148
+ var c = instance.exports.getMessageChar();
149
+ while ( c != 0 ) {
150
+ message += String.fromCharCode(c);
151
+ c = instance.exports.getMessageChar();
152
+ }
153
+ return message;
154
+ }
155
+
156
+ function printSharedRWMemory () {
157
+ const shared_rw_memory_size = instance.exports.getFieldNumLen32();
158
+ const arr = new Uint32Array(shared_rw_memory_size);
159
+ for (let j=0; j<shared_rw_memory_size; j++) {
160
+ arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
161
+ }
162
+
163
+ // If we've buffered other content, put a space in between the items
164
+ if (msgStr !== "") {
165
+ msgStr += " ";
166
+ }
167
+ // Then append the value to the message we are creating
168
+ msgStr += (fromArray32(arr).toString());
169
+ }
170
+
171
+ };
172
+
173
+ class WitnessCalculator {
174
+ constructor(instance, sanityCheck) {
175
+ this.instance = instance;
176
+
177
+ this.version = this.instance.exports.getVersion();
178
+ this.n32 = this.instance.exports.getFieldNumLen32();
179
+
180
+ this.instance.exports.getRawPrime();
181
+ const arr = new Uint32Array(this.n32);
182
+ for (let i=0; i<this.n32; i++) {
183
+ arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
184
+ }
185
+ this.prime = fromArray32(arr);
186
+
187
+ this.witnessSize = this.instance.exports.getWitnessSize();
188
+
189
+ this.sanityCheck = sanityCheck;
190
+ }
191
+
192
+ circom_version() {
193
+ return this.instance.exports.getVersion();
194
+ }
195
+
196
+ async _doCalculateWitness(input_orig, sanityCheck) {
197
+ //input is assumed to be a map from signals to arrays of bigints
198
+ this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
199
+ let prefix = "";
200
+ var input = new Object();
201
+ //console.log("Input: ", input_orig);
202
+ qualify_input(prefix,input_orig,input);
203
+ //console.log("Input after: ",input);
204
+ const keys = Object.keys(input);
205
+ var input_counter = 0;
206
+ keys.forEach( (k) => {
207
+ const h = fnvHash(k);
208
+ const hMSB = parseInt(h.slice(0,8), 16);
209
+ const hLSB = parseInt(h.slice(8,16), 16);
210
+ const fArr = flatArray(input[k]);
211
+ let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
212
+ if (signalSize < 0){
213
+ throw new Error(`Signal ${k} not found\n`);
214
+ }
215
+ if (fArr.length < signalSize) {
216
+ throw new Error(`Not enough values for input signal ${k}\n`);
217
+ }
218
+ if (fArr.length > signalSize) {
219
+ throw new Error(`Too many values for input signal ${k}\n`);
220
+ }
221
+ for (let i=0; i<fArr.length; i++) {
222
+ const arrFr = toArray32(normalize(fArr[i],this.prime),this.n32);
223
+ for (let j=0; j<this.n32; j++) {
224
+ this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
225
+ }
226
+ try {
227
+ this.instance.exports.setInputSignal(hMSB, hLSB,i);
228
+ input_counter++;
229
+ } catch (err) {
230
+ // console.log(`After adding signal ${i} of ${k}`)
231
+ throw new Error(err);
232
+ }
233
+ }
234
+
235
+ });
236
+ if (input_counter < this.instance.exports.getInputSize()) {
237
+ throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
238
+ }
239
+ }
240
+
241
+ async calculateWitness(input, sanityCheck) {
242
+
243
+ const w = [];
244
+ await this._doCalculateWitness(input, sanityCheck);
245
+
246
+ for (let i=0; i<this.witnessSize; i++) {
247
+ this.instance.exports.getWitness(i);
248
+ const arr = new Uint32Array(this.n32);
249
+ for (let j=0; j<this.n32; j++) {
250
+ arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
251
+ }
252
+ w.push(fromArray32(arr));
253
+ }
254
+
255
+ return w;
256
+ }
257
+
258
+
259
+ async calculateBinWitness(input, sanityCheck) {
260
+
261
+ const buff32 = new Uint32Array(this.witnessSize*this.n32);
262
+ const buff = new Uint8Array( buff32.buffer);
263
+ await this._doCalculateWitness(input, sanityCheck);
264
+
265
+ for (let i=0; i<this.witnessSize; i++) {
266
+ this.instance.exports.getWitness(i);
267
+ const pos = i*this.n32;
268
+ for (let j=0; j<this.n32; j++) {
269
+ buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
270
+ }
271
+ }
272
+
273
+ return buff;
274
+ }
275
+
276
+
277
+ async calculateWTNSBin(input, sanityCheck) {
278
+
279
+ const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
280
+ const buff = new Uint8Array( buff32.buffer);
281
+ await this._doCalculateWitness(input, sanityCheck);
282
+
283
+ //"wtns"
284
+ buff[0] = "w".charCodeAt(0);
285
+ buff[1] = "t".charCodeAt(0);
286
+ buff[2] = "n".charCodeAt(0);
287
+ buff[3] = "s".charCodeAt(0);
288
+
289
+ //version 2
290
+ buff32[1] = 2;
291
+
292
+ //number of sections: 2
293
+ buff32[2] = 2;
294
+
295
+ //id section 1
296
+ buff32[3] = 1;
297
+
298
+ const n8 = this.n32*4;
299
+ //id section 1 length in 64bytes
300
+ const idSection1length = 8 + n8;
301
+ const idSection1lengthHex = idSection1length.toString(16);
302
+ buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
303
+ buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
304
+
305
+ //this.n32
306
+ buff32[6] = n8;
307
+
308
+ //prime number
309
+ this.instance.exports.getRawPrime();
310
+
311
+ var pos = 7;
312
+ for (let j=0; j<this.n32; j++) {
313
+ buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
314
+ }
315
+ pos += this.n32;
316
+
317
+ // witness size
318
+ buff32[pos] = this.witnessSize;
319
+ pos++;
320
+
321
+ //id section 2
322
+ buff32[pos] = 2;
323
+ pos++;
324
+
325
+ // section 2 length
326
+ const idSection2length = n8*this.witnessSize;
327
+ const idSection2lengthHex = idSection2length.toString(16);
328
+ buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
329
+ buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
330
+
331
+ pos += 2;
332
+ for (let i=0; i<this.witnessSize; i++) {
333
+ this.instance.exports.getWitness(i);
334
+ for (let j=0; j<this.n32; j++) {
335
+ buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
336
+ }
337
+ pos += this.n32;
338
+ }
339
+
340
+ return buff;
341
+ }
342
+
343
+ }
344
+
345
+
346
+ function qualify_input_list(prefix,input,input1){
347
+ if (Array.isArray(input)) {
348
+ for (let i = 0; i<input.length; i++) {
349
+ let new_prefix = prefix + "[" + i + "]";
350
+ qualify_input_list(new_prefix,input[i],input1);
351
+ }
352
+ } else {
353
+ qualify_input(prefix,input,input1);
354
+ }
355
+ }
356
+
357
+ function qualify_input(prefix,input,input1) {
358
+ if (Array.isArray(input)) {
359
+ let a = flatArray(input);
360
+ if (a.length > 0) {
361
+ let t = typeof a[0];
362
+ for (let i = 1; i<a.length; i++) {
363
+ if (typeof a[i] != t){
364
+ throw new Error(`Types are not the same in the key ${prefix}`);
365
+ }
366
+ }
367
+ if (t == "object") {
368
+ qualify_input_list(prefix,input,input1);
369
+ } else {
370
+ input1[prefix] = input;
371
+ }
372
+ } else {
373
+ input1[prefix] = input;
374
+ }
375
+ } else if (typeof input == "object") {
376
+ const keys = Object.keys(input);
377
+ keys.forEach( (k) => {
378
+ let new_prefix = prefix == ""? k : prefix + "." + k;
379
+ qualify_input(new_prefix,input[k],input1);
380
+ });
381
+ } else {
382
+ input1[prefix] = input;
383
+ }
384
+ }
385
+
386
+ function toArray32(rem,size) {
387
+ const res = []; //new Uint32Array(size); //has no unshift
388
+ const radix = BigInt(0x100000000);
389
+ while (rem) {
390
+ res.unshift( Number(rem % radix));
391
+ rem = rem / radix;
392
+ }
393
+ if (size) {
394
+ var i = size - res.length;
395
+ while (i>0) {
396
+ res.unshift(0);
397
+ i--;
398
+ }
399
+ }
400
+ return res;
401
+ }
402
+
403
+ function fromArray32(arr) { //returns a BigInt
404
+ var res = BigInt(0);
405
+ const radix = BigInt(0x100000000);
406
+ for (let i = 0; i<arr.length; i++) {
407
+ res = res*radix + BigInt(arr[i]);
408
+ }
409
+ return res;
410
+ }
411
+
412
+ function flatArray(a) {
413
+ var res = [];
414
+ fillArray(res, a);
415
+ return res;
416
+
417
+ function fillArray(res, a) {
418
+ if (Array.isArray(a)) {
419
+ for (let i=0; i<a.length; i++) {
420
+ fillArray(res, a[i]);
421
+ }
422
+ } else {
423
+ res.push(a);
424
+ }
425
+ }
426
+ }
427
+
428
+ function normalize(n, prime) {
429
+ let res = BigInt(n) % prime;
430
+ if (res < 0) res += prime;
431
+ return res
432
+ }
433
+
434
+ function fnvHash(str) {
435
+ const uint64_max = BigInt(2) ** BigInt(64);
436
+ let hash = BigInt("0xCBF29CE484222325");
437
+ for (var i = 0; i < str.length; i++) {
438
+ hash ^= BigInt(str[i].charCodeAt());
439
+ hash *= BigInt(0x100000001B3);
440
+ hash %= uint64_max;
441
+ }
442
+ let shash = hash.toString(16);
443
+ let n = 16 - shash.length;
444
+ shash = '0'.repeat(n).concat(shash);
445
+ return shash;
446
+ }
447
+
448
+ var witnessCalculatorModule = /*@__PURE__*/getDefaultExportFromCjs(witness_calculator);
449
+
450
+ const isNode$1 = typeof process !== 'undefined' && !!process.versions?.node;
451
+ async function generateWitness(input, circuitWasm) {
452
+ let wasmBuffer;
453
+ let wc;
454
+ if (isNode$1) {
455
+ const fs = await import('fs');
456
+ const nodePath = await import('path');
457
+ const { fileURLToPath } = await import('url');
458
+ const dir = nodePath.dirname(fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href))));
459
+ const assetsDir = nodePath.resolve(dir, '..', 'assets');
460
+ wc = { default: witnessCalculatorModule };
461
+ if (circuitWasm) {
462
+ wasmBuffer = circuitWasm;
463
+ }
464
+ else {
465
+ const wasmPath = nodePath.resolve(assetsDir, 'main.wasm');
466
+ if (!fs.existsSync(wasmPath)) {
467
+ throw new Error(`Circuit WASM not found at ${wasmPath}. Run build.sh first.`);
468
+ }
469
+ wasmBuffer = fs.readFileSync(wasmPath);
470
+ }
471
+ }
472
+ else {
473
+ if (!circuitWasm) {
474
+ throw new Error('In browser, you must pass circuitWasm to PrivacyPoolSDK.init(). ' +
475
+ 'Load the circuit WASM file via fetch() and pass the ArrayBuffer.');
476
+ }
477
+ wasmBuffer = circuitWasm;
478
+ wc = { default: witnessCalculatorModule };
479
+ }
480
+ const calculator = await wc.default(wasmBuffer);
481
+ const wtns = await calculator.calculateWTNSBin(input, 0);
482
+ return wtns;
483
+ }
484
+
485
+ // @ts-ignore - snarkjs types
486
+ const isNode = typeof process !== 'undefined' && !!process.versions?.node;
487
+ /**
488
+ * Normalize BufferSource to Uint8Array. Required for @iden3/binfileutils/fastfile:
489
+ * they read via data.buffer and data.byteOffset, which ArrayBuffer does not have.
490
+ */
491
+ function toUint8Array(src) {
492
+ if (src instanceof Uint8Array)
493
+ return src;
494
+ if (src instanceof ArrayBuffer)
495
+ return new Uint8Array(src);
496
+ return new Uint8Array(src.buffer, src.byteOffset, src.byteLength);
497
+ }
498
+ async function generateProof(wtns, zkey) {
499
+ let zkeyData;
500
+ if (zkey) {
501
+ zkeyData = toUint8Array(zkey);
502
+ }
503
+ else if (isNode) {
504
+ const fs = await import('fs');
505
+ const nodePath = await import('path');
506
+ const { fileURLToPath } = await import('url');
507
+ const dir = nodePath.dirname(fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href))));
508
+ const zkeyPath = nodePath.resolve(dir, '..', 'assets', 'main_final.zkey');
509
+ if (!fs.existsSync(zkeyPath)) {
510
+ throw new Error(`Proving key not found at ${zkeyPath}. Run build.sh first.`);
511
+ }
512
+ const zkeyBuffer = fs.readFileSync(zkeyPath);
513
+ zkeyData = toUint8Array(zkeyBuffer);
514
+ }
515
+ else {
516
+ throw new Error('In browser, you must pass zkey to PrivacyPoolSDK.init(). ' +
517
+ 'Load the zkey file via fetch() and pass the ArrayBuffer.');
518
+ }
519
+ const wtnsData = toUint8Array(wtns);
520
+ const { proof, publicSignals } = await snarkjs__namespace.groth16.prove({ type: 'mem', data: zkeyData }, { type: 'mem', data: wtnsData });
521
+ return { proof, publicSignals };
522
+ }
523
+
524
+ class PrivacyPoolSDK {
525
+ constructor(wasm, options) {
526
+ this.wasm = wasm;
527
+ this.options = options;
528
+ }
529
+ /**
530
+ * Initialize the SDK by loading WASM modules.
531
+ *
532
+ * In Node.js, assets are loaded from the filesystem automatically.
533
+ * In browser, pass pre-loaded buffers via options:
534
+ * ```ts
535
+ * const sdk = await PrivacyPoolSDK.init({
536
+ * wasmBinary: await fetch('/pkg/client_sdk_wasm_bg.wasm').then(r => r.arrayBuffer()),
537
+ * circuitWasm: await fetch('/assets/main.wasm').then(r => r.arrayBuffer()),
538
+ * zkey: await fetch('/assets/main_final.zkey').then(r => r.arrayBuffer()),
539
+ * });
540
+ * ```
541
+ */
542
+ static async init(options) {
543
+ const opts = options ?? {};
544
+ const wasm = await loadWasm(opts.wasmBinary);
545
+ return new PrivacyPoolSDK(wasm, opts);
546
+ }
547
+ /**
548
+ * Generate a new coin with random nullifier and secret.
549
+ */
550
+ generateCoin() {
551
+ return this.wasm.generateCoin();
552
+ }
553
+ /**
554
+ * Generate withdrawal SNARK input from coin data and state.
555
+ */
556
+ generateWithdrawalInput(coin, state) {
557
+ const coinJson = JSON.stringify(coin);
558
+ const stateJson = JSON.stringify(state);
559
+ const resultJson = this.wasm.generateWithdrawalInput(coinJson, stateJson);
560
+ return JSON.parse(resultJson);
561
+ }
562
+ /**
563
+ * Convert a snarkjs proof JSON to hex bytes for Soroban.
564
+ */
565
+ proofToHex(proof) {
566
+ return this.wasm.proofToHex(JSON.stringify(proof));
567
+ }
568
+ /**
569
+ * Convert snarkjs public signals to hex bytes for Soroban.
570
+ */
571
+ publicToHex(publicSignals) {
572
+ return this.wasm.publicToHex(JSON.stringify(publicSignals));
573
+ }
574
+ /**
575
+ * Full withdrawal flow: generate input -> witness -> proof -> serialize.
576
+ * Returns proof_hex and public_hex ready for Soroban contract call.
577
+ */
578
+ async prepareWithdrawal(coin, state) {
579
+ const snarkInput = this.generateWithdrawalInput(coin, state);
580
+ const wtns = await generateWitness(snarkInput, this.options.circuitWasm);
581
+ const { proof, publicSignals } = await generateProof(wtns, this.options.zkey);
582
+ const proof_hex = this.proofToHex(proof);
583
+ const public_hex = this.publicToHex(publicSignals);
584
+ return { proof_hex, public_hex };
585
+ }
586
+ /**
587
+ * Calculate nullifier hash: Poseidon(nullifier)
588
+ * @param nullifier Nullifier decimal string from coin data
589
+ * @returns Hex string (0x...) of the hash bytes
590
+ */
591
+ calculateNullifierHash(nullifier) {
592
+ return this.wasm.calculateNullifierHash(nullifier);
593
+ }
594
+ }
595
+
596
+ async function main() {
597
+ const args = process.argv.slice(2);
598
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
599
+ printUsage();
600
+ process.exit(0);
601
+ }
602
+ const command = args[0];
603
+ if (command === 'withdraw') {
604
+ await handleWithdraw(args.slice(1));
605
+ }
606
+ else if (command === 'generate') {
607
+ await handleGenerate(args.slice(1));
608
+ }
609
+ else {
610
+ console.error(`Unknown command: ${command}`);
611
+ printUsage();
612
+ process.exit(1);
613
+ }
614
+ // Exit explicitly so the process doesn't hang (e.g. snarkjs workers / timers)
615
+ process.exit(0);
616
+ }
617
+ function printUsage() {
618
+ console.error(`Usage: client-sdk-cli <command> [options]
619
+
620
+ Commands:
621
+ generate Generate a new coin
622
+ --output, -o <file> Output coin to file (default: stdout)
623
+
624
+ withdraw Generate a withdrawal proof
625
+ --coin <file> Path to coin JSON file
626
+ --state <file> Path to state JSON file
627
+ --output-proof <file> Write proof hex to file
628
+ --output-public <file> Write public signals hex to file
629
+
630
+ Output (withdraw):
631
+ Prints proof_hex on first line and public_hex on second line to stdout.
632
+ `);
633
+ }
634
+ function parseArgs(args) {
635
+ const parsed = {};
636
+ for (let i = 0; i < args.length; i++) {
637
+ const arg = args[i];
638
+ if (arg.startsWith('--')) {
639
+ const key = arg.slice(2);
640
+ const value = args[i + 1];
641
+ if (value && !value.startsWith('--')) {
642
+ parsed[key] = value;
643
+ i++;
644
+ }
645
+ else {
646
+ parsed[key] = 'true';
647
+ }
648
+ }
649
+ else if (arg === '-o' && i + 1 < args.length) {
650
+ parsed['output'] = args[++i];
651
+ }
652
+ }
653
+ return parsed;
654
+ }
655
+ async function handleGenerate(args) {
656
+ const parsed = parseArgs(args);
657
+ const sdk = await PrivacyPoolSDK.init();
658
+ const coin = sdk.generateCoin();
659
+ const json = JSON.stringify(coin, null, 2);
660
+ if (parsed['output']) {
661
+ fs__namespace.writeFileSync(parsed['output'], json);
662
+ console.error(`Coin saved to: ${parsed['output']}`);
663
+ console.error(`Commitment: ${coin.commitment_hex}`);
664
+ }
665
+ else {
666
+ console.log(json);
667
+ }
668
+ }
669
+ async function handleWithdraw(args) {
670
+ const parsed = parseArgs(args);
671
+ if (!parsed['coin']) {
672
+ console.error('Error: --coin <file> is required');
673
+ process.exit(1);
674
+ }
675
+ if (!parsed['state']) {
676
+ console.error('Error: --state <file> is required');
677
+ process.exit(1);
678
+ }
679
+ const coinFile = JSON.parse(fs__namespace.readFileSync(parsed['coin'], 'utf-8'));
680
+ const coin = coinFile.coin || coinFile;
681
+ const state = JSON.parse(fs__namespace.readFileSync(parsed['state'], 'utf-8'));
682
+ const sdk = await PrivacyPoolSDK.init();
683
+ const result = await sdk.prepareWithdrawal(coin, state);
684
+ // Output proof and public hex to stdout (newline-separated)
685
+ console.log(result.proof_hex);
686
+ console.log(result.public_hex);
687
+ // Optionally write to files
688
+ if (parsed['output-proof']) {
689
+ fs__namespace.writeFileSync(parsed['output-proof'], result.proof_hex);
690
+ console.error(`Proof written to: ${parsed['output-proof']}`);
691
+ }
692
+ if (parsed['output-public']) {
693
+ fs__namespace.writeFileSync(parsed['output-public'], result.public_hex);
694
+ console.error(`Public signals written to: ${parsed['output-public']}`);
695
+ }
696
+ }
697
+ main().catch((err) => {
698
+ console.error('Error:', err.message || err);
699
+ process.exit(1);
700
+ });
701
+ //# sourceMappingURL=cli.js.map