@orbinum/proof-generator 0.3.0
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.
Potentially problematic release.
This version of @orbinum/proof-generator might be problematic. Click here for more details.
- package/README.md +170 -0
- package/circuits/disclosure.wasm +0 -0
- package/circuits/disclosure_pk.ark +0 -0
- package/circuits/transfer.wasm +0 -0
- package/circuits/transfer_pk.ark +0 -0
- package/circuits/unshield.wasm +0 -0
- package/circuits/unshield_pk.ark +0 -0
- package/dist/circuits.d.ts +16 -0
- package/dist/circuits.js +86 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +222 -0
- package/dist/proof-generator.d.ts +26 -0
- package/dist/proof-generator.js +105 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.js +46 -0
- package/dist/utils.d.ts +35 -0
- package/dist/utils.js +95 -0
- package/dist/wasm-loader.d.ts +23 -0
- package/dist/wasm-loader.js +89 -0
- package/dist/witness.d.ts +22 -0
- package/dist/witness.js +85 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Orbinum Proof Generator
|
|
2
|
+
|
|
3
|
+
> High-performance ZK-SNARK proof generator for Orbinum privacy protocol
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://www.rust-lang.org/)
|
|
8
|
+
|
|
9
|
+
## What is it?
|
|
10
|
+
|
|
11
|
+
A hybrid TypeScript/Rust/WASM library for generating compact ZK-SNARK proofs for privacy-preserving blockchain transactions. Combines **snarkjs** (witness calculation from WASM circuits) with **Rust/arkworks** (proof generation) to produce 128-byte compressed Groth16 proofs compatible with Substrate runtime.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Fast**: ~8.5s end-to-end proof generation
|
|
16
|
+
- **Compact**: 128-byte compressed proofs (50% smaller than snarkjs)
|
|
17
|
+
- **Privacy**: Unshield, Private Transfer, and Disclosure circuits
|
|
18
|
+
- **Type-Safe**: Full TypeScript API with comprehensive types
|
|
19
|
+
- **Production Ready**: 28/28 tests passing, pre-commit hooks
|
|
20
|
+
- **Auto-Build**: Rust binary compiles automatically on `npm install`
|
|
21
|
+
- **WASM Support**: Browser-compatible witness calculation via Circom WASM circuits
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @orbinum/proof-generator
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { generateProof, CircuitType } from '@orbinum/proof-generator';
|
|
31
|
+
|
|
32
|
+
const { proof, publicSignals } = await generateProof(CircuitType.Unshield, circuitInputs);
|
|
33
|
+
// proof: 0x... (128 bytes)
|
|
34
|
+
// publicSignals: ['0x...', '0x...', ...] (5 elements)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
See [installation](docs/installation.md) for detailed setup and [usage](docs/usage.md) for complete API reference.
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌─────────────────────────────────────────────────┐
|
|
43
|
+
│ Circuit Inputs (JSON) │
|
|
44
|
+
└────────────────┬────────────────────────────────┘
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
┌─────────────────────────────────────────────────┐
|
|
48
|
+
│ TypeScript (snarkjs + WASM) │
|
|
49
|
+
│ • Witness calculation from Circom WASM circuit │
|
|
50
|
+
│ • ~500ms, produces 11,808 elements │
|
|
51
|
+
└────────────────┬────────────────────────────────┘
|
|
52
|
+
│
|
|
53
|
+
▼
|
|
54
|
+
┌─────────────────────────────────────────────────┐
|
|
55
|
+
│ Rust Binary (arkworks) │
|
|
56
|
+
│ • Groth16 proof generation from .ark key │
|
|
57
|
+
│ • ~8s, produces compressed proof │
|
|
58
|
+
└────────────────┬────────────────────────────────┘
|
|
59
|
+
│
|
|
60
|
+
▼
|
|
61
|
+
┌─────────────────────────────────────────────────┐
|
|
62
|
+
│ Output: 128-byte proof + public signals │
|
|
63
|
+
│ • Ready for Substrate runtime submission │
|
|
64
|
+
└─────────────────────────────────────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Why Hybrid?
|
|
68
|
+
|
|
69
|
+
- **Compatibility**: snarkjs handles Circom WASM circuits correctly (browser/Node.js)
|
|
70
|
+
- **Efficiency**: arkworks generates compact proofs required by runtime
|
|
71
|
+
- **Performance**: Rust is 20-30% faster than pure JavaScript
|
|
72
|
+
- **Maintainability**: Leverages best tools from both ecosystems
|
|
73
|
+
- **Flexibility**: WASM witness calculation works in any environment
|
|
74
|
+
|
|
75
|
+
## Supported Circuits
|
|
76
|
+
|
|
77
|
+
| Circuit | Public Signals | Description |
|
|
78
|
+
| -------------- | -------------- | --------------------------------------------- |
|
|
79
|
+
| **Unshield** | 5 | Withdraw from shielded pool to public address |
|
|
80
|
+
| **Transfer** | 5 | Private transfer (2-in-2-out UTXO model) |
|
|
81
|
+
| **Disclosure** | 4 | Selective revelation for compliance |
|
|
82
|
+
|
|
83
|
+
## Requirements
|
|
84
|
+
|
|
85
|
+
- **Node.js**: ≥ 22.0.0
|
|
86
|
+
- **Rust**: 1.88.0 (automatically managed via `rust-toolchain.toml`)
|
|
87
|
+
- **RAM**: ~2GB for proof generation
|
|
88
|
+
- **Circuits**: Circuit files downloaded automatically from [circuits releases](https://github.com/orbinum/circuits/releases) during `npm install`
|
|
89
|
+
|
|
90
|
+
## Project Structure
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
proof-generator/
|
|
94
|
+
├── src/ # TypeScript API
|
|
95
|
+
│ ├── index.ts # Main exports
|
|
96
|
+
│ ├── circuits.ts # Circuit configs
|
|
97
|
+
│ └── proof-generator.ts # Rust binary wrapper
|
|
98
|
+
├── rust/ # Rust implementation (compiled to native binary)
|
|
99
|
+
│ ├── src/
|
|
100
|
+
│ │ ├── lib.rs # Core library
|
|
101
|
+
│ │ └── bin/ # CLI binary
|
|
102
|
+
│ └── rust-toolchain.toml
|
|
103
|
+
├── pkg/ # WASM output (generated by wasm-pack build)
|
|
104
|
+
│ ├── orbinum_proof_generator.js
|
|
105
|
+
│ ├── orbinum_proof_generator_bg.wasm
|
|
106
|
+
│ └── orbinum_proof_generator.d.ts
|
|
107
|
+
├── circuits/ # ZK circuits (auto-downloaded from circuits repo)
|
|
108
|
+
│ ├── *.wasm # Circom witness calculators (used by snarkjs)
|
|
109
|
+
│ └── *_pk.ark # Proving keys in arkworks format (used by Rust)
|
|
110
|
+
├── tests/ # Jest tests (28 tests)
|
|
111
|
+
└── docs/
|
|
112
|
+
├── installation.md
|
|
113
|
+
├── usage.md
|
|
114
|
+
├── architecture.md
|
|
115
|
+
└── testing.md
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Install dependencies
|
|
122
|
+
npm install
|
|
123
|
+
|
|
124
|
+
# Build everything (Rust + TypeScript)
|
|
125
|
+
npm run build
|
|
126
|
+
|
|
127
|
+
# Run tests
|
|
128
|
+
npm test
|
|
129
|
+
|
|
130
|
+
# Format code
|
|
131
|
+
npm run format
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Circuit Artifacts
|
|
135
|
+
|
|
136
|
+
Circuit artifacts are automatically downloaded from [orbinum/circuits releases](https://github.com/orbinum/circuits/releases) during `npm install`.
|
|
137
|
+
|
|
138
|
+
**Required files per circuit:**
|
|
139
|
+
|
|
140
|
+
- `{circuit}.wasm` - Witness calculator (used by snarkjs)
|
|
141
|
+
- `{circuit}_pk.ark` - Proving key in arkworks format (used by Rust)
|
|
142
|
+
|
|
143
|
+
**Note:** The `.zkey` files (snarkjs format) are NOT needed since this generator uses arkworks to create proofs.
|
|
144
|
+
|
|
145
|
+
**Manual configuration:**
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Specify circuits version
|
|
149
|
+
export CIRCUITS_VERSION=v0.2.0
|
|
150
|
+
npm install
|
|
151
|
+
|
|
152
|
+
# Or download manually
|
|
153
|
+
node scripts/download-artifacts.js
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Links
|
|
157
|
+
|
|
158
|
+
- **Documentation**: [docs.orbinum.network](https://docs.orbinum.network)
|
|
159
|
+
- **Main Repository**: [github.com/orbinum/node](https://github.com/orbinum/node)
|
|
160
|
+
- **Issues**: [github.com/orbinum/proof-generator/issues](https://github.com/orbinum/proof-generator/issues)
|
|
161
|
+
- **Circuits**: [github.com/orbinum/circuits](https://github.com/orbinum/circuits)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
This project is dual-licensed under:
|
|
166
|
+
|
|
167
|
+
- **Apache License 2.0** ([LICENSE-APACHE2](LICENSE-APACHE2))
|
|
168
|
+
- **GNU General Public License v3.0** ([LICENSE-GPL3](LICENSE-GPL3))
|
|
169
|
+
|
|
170
|
+
You may choose either license for your use case.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Configuration
|
|
3
|
+
*
|
|
4
|
+
* Defines paths and metadata for all supported circuits
|
|
5
|
+
*/
|
|
6
|
+
import { CircuitType, CircuitConfig } from './types';
|
|
7
|
+
/** Circuit configurations */
|
|
8
|
+
export declare const CIRCUIT_CONFIGS: Record<CircuitType, CircuitConfig>;
|
|
9
|
+
/**
|
|
10
|
+
* Get circuit configuration
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCircuitConfig(circuitType: CircuitType): CircuitConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Validate that circuit artifacts exist
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateCircuitArtifacts(config: CircuitConfig): void;
|
package/dist/circuits.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Circuit Configuration
|
|
4
|
+
*
|
|
5
|
+
* Defines paths and metadata for all supported circuits
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.CIRCUIT_CONFIGS = void 0;
|
|
42
|
+
exports.getCircuitConfig = getCircuitConfig;
|
|
43
|
+
exports.validateCircuitArtifacts = validateCircuitArtifacts;
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const types_1 = require("./types");
|
|
46
|
+
/** Base path to circuits directory (relative to this file) */
|
|
47
|
+
const CIRCUITS_BASE = path.join(__dirname, '../circuits');
|
|
48
|
+
/** Circuit configurations */
|
|
49
|
+
exports.CIRCUIT_CONFIGS = {
|
|
50
|
+
[types_1.CircuitType.Unshield]: {
|
|
51
|
+
name: 'unshield',
|
|
52
|
+
wasmPath: path.join(CIRCUITS_BASE, 'unshield.wasm'),
|
|
53
|
+
provingKeyPath: path.join(CIRCUITS_BASE, 'unshield_pk.ark'),
|
|
54
|
+
expectedPublicSignals: 5, // merkle_root, nullifier, amount, recipient, asset_id
|
|
55
|
+
},
|
|
56
|
+
[types_1.CircuitType.Transfer]: {
|
|
57
|
+
name: 'transfer',
|
|
58
|
+
wasmPath: path.join(CIRCUITS_BASE, 'transfer.wasm'),
|
|
59
|
+
provingKeyPath: path.join(CIRCUITS_BASE, 'transfer_pk.ark'),
|
|
60
|
+
expectedPublicSignals: 5, // merkle_root, input_nullifiers(2), output_commitments(2)
|
|
61
|
+
},
|
|
62
|
+
[types_1.CircuitType.Disclosure]: {
|
|
63
|
+
name: 'disclosure',
|
|
64
|
+
wasmPath: path.join(CIRCUITS_BASE, 'disclosure.wasm'),
|
|
65
|
+
provingKeyPath: path.join(CIRCUITS_BASE, 'disclosure_pk.ark'),
|
|
66
|
+
expectedPublicSignals: 4, // commitment, vk_hash, mask, revealed_owner_hash
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Get circuit configuration
|
|
71
|
+
*/
|
|
72
|
+
function getCircuitConfig(circuitType) {
|
|
73
|
+
return exports.CIRCUIT_CONFIGS[circuitType];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate that circuit artifacts exist
|
|
77
|
+
*/
|
|
78
|
+
function validateCircuitArtifacts(config) {
|
|
79
|
+
const fs = require('fs');
|
|
80
|
+
if (!fs.existsSync(config.wasmPath)) {
|
|
81
|
+
throw new Error(`WASM not found: ${config.wasmPath}`);
|
|
82
|
+
}
|
|
83
|
+
if (!fs.existsSync(config.provingKeyPath)) {
|
|
84
|
+
throw new Error(`Proving key not found: ${config.provingKeyPath}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orbinum Proof Generator
|
|
3
|
+
*
|
|
4
|
+
* Main API for generating ZK-SNARK proofs for Orbinum circuits
|
|
5
|
+
*/
|
|
6
|
+
import { CircuitType, CircuitInputs, ProofResult } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Generate a ZK-SNARK proof for an Orbinum circuit
|
|
9
|
+
*
|
|
10
|
+
* This is the main entry point for proof generation. It:
|
|
11
|
+
* 1. Validates inputs and circuit artifacts
|
|
12
|
+
* 2. Calculates witness using snarkjs
|
|
13
|
+
* 3. Generates proof using Rust/arkworks
|
|
14
|
+
* 4. Returns compressed 128-byte proof + public signals
|
|
15
|
+
*
|
|
16
|
+
* @param circuitType - Type of circuit (Unshield, Transfer, Disclosure)
|
|
17
|
+
* @param inputs - Circuit inputs (structure depends on circuit type)
|
|
18
|
+
* @param options - Optional configuration
|
|
19
|
+
* @returns Proof result with proof hex and public signals
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const result = await generateProof(CircuitType.Unshield, {
|
|
24
|
+
* merkle_root: '...',
|
|
25
|
+
* nullifier: '...',
|
|
26
|
+
* amount: '100',
|
|
27
|
+
* recipient: '...',
|
|
28
|
+
* asset_id: '0',
|
|
29
|
+
* note_value: '100',
|
|
30
|
+
* note_asset_id: '0',
|
|
31
|
+
* note_owner: '...',
|
|
32
|
+
* note_blinding: '...',
|
|
33
|
+
* spending_key: '...',
|
|
34
|
+
* path_elements: [...],
|
|
35
|
+
* path_indices: [...]
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* console.log('Proof:', result.proof); // 0x... (128 bytes)
|
|
39
|
+
* console.log('Public signals:', result.publicSignals);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateProof(circuitType: CircuitType, inputs: CircuitInputs, options?: {
|
|
43
|
+
verbose?: boolean;
|
|
44
|
+
validateArtifacts?: boolean;
|
|
45
|
+
}): Promise<ProofResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Quick check if proof generator is ready to use
|
|
48
|
+
*/
|
|
49
|
+
export declare function isReady(): boolean;
|
|
50
|
+
export * from './types';
|
|
51
|
+
export * from './circuits';
|
|
52
|
+
export { calculateWitness, calculateWitnessToFile } from './witness';
|
|
53
|
+
export { generateProofFromWitness, isBinaryBuilt, getBinaryPath } from './proof-generator';
|
|
54
|
+
export * from './utils';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Orbinum Proof Generator
|
|
4
|
+
*
|
|
5
|
+
* Main API for generating ZK-SNARK proofs for Orbinum circuits
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
41
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.getBinaryPath = exports.isBinaryBuilt = exports.generateProofFromWitness = exports.calculateWitnessToFile = exports.calculateWitness = void 0;
|
|
45
|
+
exports.generateProof = generateProof;
|
|
46
|
+
exports.isReady = isReady;
|
|
47
|
+
const types_1 = require("./types");
|
|
48
|
+
const circuits_1 = require("./circuits");
|
|
49
|
+
const witness_1 = require("./witness");
|
|
50
|
+
const proof_generator_1 = require("./proof-generator");
|
|
51
|
+
const utils_1 = require("./utils");
|
|
52
|
+
const wasm_loader_1 = require("./wasm-loader");
|
|
53
|
+
// Auto-detect environment
|
|
54
|
+
const USE_WASM = (0, wasm_loader_1.isBrowser)();
|
|
55
|
+
/**
|
|
56
|
+
* Generate a ZK-SNARK proof for an Orbinum circuit
|
|
57
|
+
*
|
|
58
|
+
* This is the main entry point for proof generation. It:
|
|
59
|
+
* 1. Validates inputs and circuit artifacts
|
|
60
|
+
* 2. Calculates witness using snarkjs
|
|
61
|
+
* 3. Generates proof using Rust/arkworks
|
|
62
|
+
* 4. Returns compressed 128-byte proof + public signals
|
|
63
|
+
*
|
|
64
|
+
* @param circuitType - Type of circuit (Unshield, Transfer, Disclosure)
|
|
65
|
+
* @param inputs - Circuit inputs (structure depends on circuit type)
|
|
66
|
+
* @param options - Optional configuration
|
|
67
|
+
* @returns Proof result with proof hex and public signals
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const result = await generateProof(CircuitType.Unshield, {
|
|
72
|
+
* merkle_root: '...',
|
|
73
|
+
* nullifier: '...',
|
|
74
|
+
* amount: '100',
|
|
75
|
+
* recipient: '...',
|
|
76
|
+
* asset_id: '0',
|
|
77
|
+
* note_value: '100',
|
|
78
|
+
* note_asset_id: '0',
|
|
79
|
+
* note_owner: '...',
|
|
80
|
+
* note_blinding: '...',
|
|
81
|
+
* spending_key: '...',
|
|
82
|
+
* path_elements: [...],
|
|
83
|
+
* path_indices: [...]
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* console.log('Proof:', result.proof); // 0x... (128 bytes)
|
|
87
|
+
* console.log('Public signals:', result.publicSignals);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async function generateProof(circuitType, inputs, options = {}) {
|
|
91
|
+
const { verbose = false, validateArtifacts = true } = options;
|
|
92
|
+
// Step 0: Initialize WASM if in browser
|
|
93
|
+
if (USE_WASM) {
|
|
94
|
+
if (verbose) {
|
|
95
|
+
console.log('🌐 Browser detected: using WASM backend');
|
|
96
|
+
}
|
|
97
|
+
await (0, wasm_loader_1.initWasm)();
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Check if binary is built (Node.js only)
|
|
101
|
+
if (!(0, proof_generator_1.isBinaryBuilt)()) {
|
|
102
|
+
throw new types_1.ProofGenerationError('Rust binary not built. Run: npm run build:rust');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Step 1: Get circuit config
|
|
106
|
+
const config = (0, circuits_1.getCircuitConfig)(circuitType);
|
|
107
|
+
if (!config) {
|
|
108
|
+
throw new types_1.CircuitNotFoundError(circuitType);
|
|
109
|
+
}
|
|
110
|
+
if (verbose) {
|
|
111
|
+
console.log(`\n🔐 Generating proof for circuit: ${config.name}`);
|
|
112
|
+
console.log(` WASM: ${config.wasmPath}`);
|
|
113
|
+
console.log(` Proving key: ${config.provingKeyPath}`);
|
|
114
|
+
}
|
|
115
|
+
// Step 2: Validate inputs
|
|
116
|
+
try {
|
|
117
|
+
(0, utils_1.validateInputs)(inputs);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
throw new types_1.InvalidInputsError(error.message);
|
|
121
|
+
}
|
|
122
|
+
// Step 3: Validate artifacts (optional)
|
|
123
|
+
if (validateArtifacts) {
|
|
124
|
+
try {
|
|
125
|
+
(0, circuits_1.validateCircuitArtifacts)(config);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
throw new types_1.CircuitNotFoundError(circuitType);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Step 4: Calculate witness with snarkjs
|
|
132
|
+
if (verbose) {
|
|
133
|
+
console.log('\n📊 Step 1: Calculating witness...');
|
|
134
|
+
}
|
|
135
|
+
let witnessData;
|
|
136
|
+
try {
|
|
137
|
+
witnessData = await (0, witness_1.calculateWitness)(inputs, config.wasmPath);
|
|
138
|
+
if (verbose) {
|
|
139
|
+
console.log(` ✅ Witness calculated: ${witnessData.witness.length} elements`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw new types_1.WitnessCalculationError(error.message);
|
|
144
|
+
}
|
|
145
|
+
// Step 5: Generate proof (WASM or Rust binary)
|
|
146
|
+
if (verbose) {
|
|
147
|
+
console.log(`\n🔐 Step 2: Generating proof with ${USE_WASM ? 'WASM' : 'Rust/arkworks'}...`);
|
|
148
|
+
}
|
|
149
|
+
let proofResult;
|
|
150
|
+
try {
|
|
151
|
+
if (USE_WASM) {
|
|
152
|
+
// Browser: Use WASM
|
|
153
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
154
|
+
const provingKeyBytes = await fs.readFile(config.provingKeyPath);
|
|
155
|
+
const output = await (0, wasm_loader_1.generateProofWasm)(circuitType.toLowerCase(), JSON.stringify(witnessData.witness), new Uint8Array(provingKeyBytes));
|
|
156
|
+
proofResult = {
|
|
157
|
+
proof: output.proof,
|
|
158
|
+
publicSignals: output.publicSignals,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Node.js: Use Rust binary
|
|
163
|
+
proofResult = await (0, proof_generator_1.generateProofFromWitness)(witnessData, config.provingKeyPath, config.expectedPublicSignals);
|
|
164
|
+
}
|
|
165
|
+
if (verbose) {
|
|
166
|
+
console.log(` ✅ Proof generated: ${(0, utils_1.formatProofHex)(proofResult.proof, 32)}`);
|
|
167
|
+
console.log(` ✅ Public signals: ${proofResult.publicSignals.length}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
throw new types_1.ProofGenerationError(error.message);
|
|
172
|
+
}
|
|
173
|
+
// Step 6: Validate public signals count
|
|
174
|
+
try {
|
|
175
|
+
(0, utils_1.validatePublicSignals)(proofResult.publicSignals, config.expectedPublicSignals);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
throw new types_1.ProofGenerationError(error.message);
|
|
179
|
+
}
|
|
180
|
+
if (verbose) {
|
|
181
|
+
console.log('\n✅ Proof generation completed successfully!\n');
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
proof: proofResult.proof,
|
|
185
|
+
publicSignals: proofResult.publicSignals,
|
|
186
|
+
circuitType,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Quick check if proof generator is ready to use
|
|
191
|
+
*/
|
|
192
|
+
function isReady() {
|
|
193
|
+
try {
|
|
194
|
+
// In browser, check if WASM is available
|
|
195
|
+
if (USE_WASM) {
|
|
196
|
+
// WASM gets initialized on first use
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
// Node.js: Check if Rust binary is built
|
|
200
|
+
if (!(0, proof_generator_1.isBinaryBuilt)()) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
// Check if at least one circuit has artifacts
|
|
204
|
+
const unshieldConfig = (0, circuits_1.getCircuitConfig)(types_1.CircuitType.Unshield);
|
|
205
|
+
(0, circuits_1.validateCircuitArtifacts)(unshieldConfig);
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Re-export types and utilities for convenience
|
|
213
|
+
__exportStar(require("./types"), exports);
|
|
214
|
+
__exportStar(require("./circuits"), exports);
|
|
215
|
+
var witness_2 = require("./witness");
|
|
216
|
+
Object.defineProperty(exports, "calculateWitness", { enumerable: true, get: function () { return witness_2.calculateWitness; } });
|
|
217
|
+
Object.defineProperty(exports, "calculateWitnessToFile", { enumerable: true, get: function () { return witness_2.calculateWitnessToFile; } });
|
|
218
|
+
var proof_generator_2 = require("./proof-generator");
|
|
219
|
+
Object.defineProperty(exports, "generateProofFromWitness", { enumerable: true, get: function () { return proof_generator_2.generateProofFromWitness; } });
|
|
220
|
+
Object.defineProperty(exports, "isBinaryBuilt", { enumerable: true, get: function () { return proof_generator_2.isBinaryBuilt; } });
|
|
221
|
+
Object.defineProperty(exports, "getBinaryPath", { enumerable: true, get: function () { return proof_generator_2.getBinaryPath; } });
|
|
222
|
+
__exportStar(require("./utils"), exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Generation Module
|
|
3
|
+
*
|
|
4
|
+
* Uses Rust binary to generate Groth16 proofs from witness
|
|
5
|
+
*/
|
|
6
|
+
import { WitnessData } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Generate proof using Rust/arkworks
|
|
9
|
+
*
|
|
10
|
+
* @param witnessData - Witness data (hex-encoded)
|
|
11
|
+
* @param provingKeyPath - Path to .ark proving key
|
|
12
|
+
* @param numPublicSignals - Number of public signals (optional, defaults to 5)
|
|
13
|
+
* @returns Object with proof (hex) and public signals
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateProofFromWitness(witnessData: WitnessData, provingKeyPath: string, numPublicSignals?: number): Promise<{
|
|
16
|
+
proof: string;
|
|
17
|
+
publicSignals: string[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if Rust binary is built
|
|
21
|
+
*/
|
|
22
|
+
export declare function isBinaryBuilt(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Get binary path (for debugging)
|
|
25
|
+
*/
|
|
26
|
+
export declare function getBinaryPath(): string;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Proof Generation Module
|
|
4
|
+
*
|
|
5
|
+
* Uses Rust binary to generate Groth16 proofs from witness
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.generateProofFromWitness = generateProofFromWitness;
|
|
42
|
+
exports.isBinaryBuilt = isBinaryBuilt;
|
|
43
|
+
exports.getBinaryPath = getBinaryPath;
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const utils_1 = require("./utils");
|
|
48
|
+
/** Path to Rust binary */
|
|
49
|
+
const BINARY_PATH = path.join(__dirname, '../rust/target/release/generate-proof-from-witness');
|
|
50
|
+
/**
|
|
51
|
+
* Generate proof using Rust/arkworks
|
|
52
|
+
*
|
|
53
|
+
* @param witnessData - Witness data (hex-encoded)
|
|
54
|
+
* @param provingKeyPath - Path to .ark proving key
|
|
55
|
+
* @param numPublicSignals - Number of public signals (optional, defaults to 5)
|
|
56
|
+
* @returns Object with proof (hex) and public signals
|
|
57
|
+
*/
|
|
58
|
+
async function generateProofFromWitness(witnessData, provingKeyPath, numPublicSignals) {
|
|
59
|
+
// Check if binary exists
|
|
60
|
+
if (!fs.existsSync(BINARY_PATH)) {
|
|
61
|
+
throw new Error(`Rust binary not found at: ${BINARY_PATH}\n` +
|
|
62
|
+
'Run: cd orbinum-proof-generator && npm run build:rust');
|
|
63
|
+
}
|
|
64
|
+
// Write witness to temp file with num_public_signals
|
|
65
|
+
const witnessJsonPath = `/tmp/witness_${Date.now()}.json`;
|
|
66
|
+
try {
|
|
67
|
+
const witnessWithMeta = {
|
|
68
|
+
...witnessData,
|
|
69
|
+
num_public_signals: numPublicSignals,
|
|
70
|
+
};
|
|
71
|
+
fs.writeFileSync(witnessJsonPath, JSON.stringify(witnessWithMeta, null, 2));
|
|
72
|
+
// Execute Rust binary (numPublicSignals can also be passed as CLI arg)
|
|
73
|
+
const result = (0, child_process_1.execSync)(`${BINARY_PATH} ${witnessJsonPath} ${provingKeyPath}`, {
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
maxBuffer: 10 * 1024 * 1024, // 10 MB
|
|
76
|
+
stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr to reduce noise in tests
|
|
77
|
+
});
|
|
78
|
+
// Parse output
|
|
79
|
+
const output = JSON.parse(result);
|
|
80
|
+
// Validate proof size (should be 128 bytes)
|
|
81
|
+
(0, utils_1.validateProofSize)(output.proof);
|
|
82
|
+
return {
|
|
83
|
+
proof: output.proof,
|
|
84
|
+
publicSignals: output.public_signals,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
// Cleanup temp file
|
|
89
|
+
if (fs.existsSync(witnessJsonPath)) {
|
|
90
|
+
fs.unlinkSync(witnessJsonPath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if Rust binary is built
|
|
96
|
+
*/
|
|
97
|
+
function isBinaryBuilt() {
|
|
98
|
+
return fs.existsSync(BINARY_PATH);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get binary path (for debugging)
|
|
102
|
+
*/
|
|
103
|
+
function getBinaryPath() {
|
|
104
|
+
return BINARY_PATH;
|
|
105
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Orbinum Proof Generator
|
|
3
|
+
*/
|
|
4
|
+
/** Circuit types supported by Orbinum */
|
|
5
|
+
export declare enum CircuitType {
|
|
6
|
+
Unshield = "unshield",
|
|
7
|
+
Transfer = "transfer",
|
|
8
|
+
Disclosure = "disclosure"
|
|
9
|
+
}
|
|
10
|
+
/** Circuit input: any key-value pairs (circuit-specific) */
|
|
11
|
+
export type CircuitInputs = Record<string, string | string[] | number | number[]>;
|
|
12
|
+
/** Proof generation result */
|
|
13
|
+
export interface ProofResult {
|
|
14
|
+
/** Compressed proof bytes (128 bytes as hex string) */
|
|
15
|
+
proof: string;
|
|
16
|
+
/** Public signals (circuit outputs) */
|
|
17
|
+
publicSignals: string[];
|
|
18
|
+
/** Circuit type used */
|
|
19
|
+
circuitType: CircuitType;
|
|
20
|
+
}
|
|
21
|
+
/** Circuit configuration */
|
|
22
|
+
export interface CircuitConfig {
|
|
23
|
+
/** Circuit name (e.g., 'unshield') */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Path to WASM file */
|
|
26
|
+
wasmPath: string;
|
|
27
|
+
/** Path to .ark proving key */
|
|
28
|
+
provingKeyPath: string;
|
|
29
|
+
/** Expected number of public signals */
|
|
30
|
+
expectedPublicSignals: number;
|
|
31
|
+
}
|
|
32
|
+
/** Witness data (hex-encoded field elements) */
|
|
33
|
+
export interface WitnessData {
|
|
34
|
+
/** Array of hex-encoded witness elements (little-endian) */
|
|
35
|
+
witness: string[];
|
|
36
|
+
}
|
|
37
|
+
/** Error types */
|
|
38
|
+
export declare class ProofGeneratorError extends Error {
|
|
39
|
+
readonly code: string;
|
|
40
|
+
constructor(message: string, code: string);
|
|
41
|
+
}
|
|
42
|
+
export declare class WitnessCalculationError extends ProofGeneratorError {
|
|
43
|
+
constructor(message: string);
|
|
44
|
+
}
|
|
45
|
+
export declare class ProofGenerationError extends ProofGeneratorError {
|
|
46
|
+
constructor(message: string);
|
|
47
|
+
}
|
|
48
|
+
export declare class CircuitNotFoundError extends ProofGeneratorError {
|
|
49
|
+
constructor(circuitType: CircuitType);
|
|
50
|
+
}
|
|
51
|
+
export declare class InvalidInputsError extends ProofGeneratorError {
|
|
52
|
+
constructor(message: string);
|
|
53
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types for Orbinum Proof Generator
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.InvalidInputsError = exports.CircuitNotFoundError = exports.ProofGenerationError = exports.WitnessCalculationError = exports.ProofGeneratorError = exports.CircuitType = void 0;
|
|
7
|
+
/** Circuit types supported by Orbinum */
|
|
8
|
+
var CircuitType;
|
|
9
|
+
(function (CircuitType) {
|
|
10
|
+
CircuitType["Unshield"] = "unshield";
|
|
11
|
+
CircuitType["Transfer"] = "transfer";
|
|
12
|
+
CircuitType["Disclosure"] = "disclosure";
|
|
13
|
+
})(CircuitType || (exports.CircuitType = CircuitType = {}));
|
|
14
|
+
/** Error types */
|
|
15
|
+
class ProofGeneratorError extends Error {
|
|
16
|
+
constructor(message, code) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.name = 'ProofGeneratorError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.ProofGeneratorError = ProofGeneratorError;
|
|
23
|
+
class WitnessCalculationError extends ProofGeneratorError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message, 'WITNESS_CALCULATION_FAILED');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.WitnessCalculationError = WitnessCalculationError;
|
|
29
|
+
class ProofGenerationError extends ProofGeneratorError {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message, 'PROOF_GENERATION_FAILED');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.ProofGenerationError = ProofGenerationError;
|
|
35
|
+
class CircuitNotFoundError extends ProofGeneratorError {
|
|
36
|
+
constructor(circuitType) {
|
|
37
|
+
super(`Circuit not found: ${circuitType}`, 'CIRCUIT_NOT_FOUND');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.CircuitNotFoundError = CircuitNotFoundError;
|
|
41
|
+
class InvalidInputsError extends ProofGeneratorError {
|
|
42
|
+
constructor(message) {
|
|
43
|
+
super(message, 'INVALID_INPUTS');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.InvalidInputsError = InvalidInputsError;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for proof generation
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert bigint to hex string (little-endian, 32 bytes)
|
|
6
|
+
*/
|
|
7
|
+
export declare function bigIntToHexLE(value: bigint): string;
|
|
8
|
+
/**
|
|
9
|
+
* Convert witness array to hex little-endian format
|
|
10
|
+
*/
|
|
11
|
+
export declare function witnessToHexLE(witness: bigint[]): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Validate circuit inputs structure
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateInputs(inputs: Record<string, any>): void;
|
|
16
|
+
/**
|
|
17
|
+
* Format proof hex for display (truncated)
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatProofHex(proofHex: string, maxLength?: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Convert hex string to bytes
|
|
22
|
+
*/
|
|
23
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
24
|
+
/**
|
|
25
|
+
* Convert bytes to hex string
|
|
26
|
+
*/
|
|
27
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
28
|
+
/**
|
|
29
|
+
* Validate proof size (should be 128 bytes)
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateProofSize(proofHex: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Validate public signals count
|
|
34
|
+
*/
|
|
35
|
+
export declare function validatePublicSignals(signals: string[], expected: number): void;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for proof generation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.bigIntToHexLE = bigIntToHexLE;
|
|
7
|
+
exports.witnessToHexLE = witnessToHexLE;
|
|
8
|
+
exports.validateInputs = validateInputs;
|
|
9
|
+
exports.formatProofHex = formatProofHex;
|
|
10
|
+
exports.hexToBytes = hexToBytes;
|
|
11
|
+
exports.bytesToHex = bytesToHex;
|
|
12
|
+
exports.validateProofSize = validateProofSize;
|
|
13
|
+
exports.validatePublicSignals = validatePublicSignals;
|
|
14
|
+
/**
|
|
15
|
+
* Convert bigint to hex string (little-endian, 32 bytes)
|
|
16
|
+
*/
|
|
17
|
+
function bigIntToHexLE(value) {
|
|
18
|
+
// Convert to hex (big-endian)
|
|
19
|
+
let hex = value.toString(16).padStart(64, '0');
|
|
20
|
+
// Reverse byte order to little-endian
|
|
21
|
+
const bytes = [];
|
|
22
|
+
for (let i = hex.length - 2; i >= 0; i -= 2) {
|
|
23
|
+
bytes.push(hex.substr(i, 2));
|
|
24
|
+
}
|
|
25
|
+
return '0x' + bytes.join('');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert witness array to hex little-endian format
|
|
29
|
+
*/
|
|
30
|
+
function witnessToHexLE(witness) {
|
|
31
|
+
return witness.map(w => bigIntToHexLE(w));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate circuit inputs structure
|
|
35
|
+
*/
|
|
36
|
+
function validateInputs(inputs) {
|
|
37
|
+
if (!inputs || typeof inputs !== 'object') {
|
|
38
|
+
throw new Error('Inputs must be an object');
|
|
39
|
+
}
|
|
40
|
+
// Check for common issues
|
|
41
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
42
|
+
if (value === undefined || value === null) {
|
|
43
|
+
throw new Error(`Input "${key}" is undefined or null`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Format proof hex for display (truncated)
|
|
49
|
+
*/
|
|
50
|
+
function formatProofHex(proofHex, maxLength = 32) {
|
|
51
|
+
if (proofHex.length <= maxLength) {
|
|
52
|
+
return proofHex;
|
|
53
|
+
}
|
|
54
|
+
const start = proofHex.slice(0, maxLength / 2);
|
|
55
|
+
const end = proofHex.slice(-maxLength / 2);
|
|
56
|
+
return `${start}...${end}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Convert hex string to bytes
|
|
60
|
+
*/
|
|
61
|
+
function hexToBytes(hex) {
|
|
62
|
+
// Remove 0x prefix if present
|
|
63
|
+
const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
64
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
65
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
66
|
+
bytes[i] = parseInt(cleanHex.substr(i * 2, 2), 16);
|
|
67
|
+
}
|
|
68
|
+
return bytes;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Convert bytes to hex string
|
|
72
|
+
*/
|
|
73
|
+
function bytesToHex(bytes) {
|
|
74
|
+
return ('0x' +
|
|
75
|
+
Array.from(bytes)
|
|
76
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
77
|
+
.join(''));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate proof size (should be 128 bytes)
|
|
81
|
+
*/
|
|
82
|
+
function validateProofSize(proofHex) {
|
|
83
|
+
const bytes = hexToBytes(proofHex);
|
|
84
|
+
if (bytes.length !== 128) {
|
|
85
|
+
throw new Error(`Invalid proof size: expected 128 bytes, got ${bytes.length} bytes`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate public signals count
|
|
90
|
+
*/
|
|
91
|
+
function validatePublicSignals(signals, expected) {
|
|
92
|
+
if (signals.length !== expected) {
|
|
93
|
+
throw new Error(`Invalid public signals count: expected ${expected}, got ${signals.length}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WASM loader for browser environments
|
|
3
|
+
*
|
|
4
|
+
* This module provides a browser-compatible interface for proof generation
|
|
5
|
+
* using the WASM-compiled Rust backend.
|
|
6
|
+
*/
|
|
7
|
+
interface WasmProofOutput {
|
|
8
|
+
proof: string;
|
|
9
|
+
publicSignals: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Initialize WASM module (browser only)
|
|
13
|
+
*/
|
|
14
|
+
export declare function initWasm(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Generate proof using WASM (browser only)
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateProofWasm(circuitType: 'unshield' | 'transfer' | 'disclosure', witnessJson: string, provingKeyBytes: Uint8Array): Promise<WasmProofOutput>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if running in browser environment
|
|
21
|
+
*/
|
|
22
|
+
export declare function isBrowser(): boolean;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WASM loader for browser environments
|
|
4
|
+
*
|
|
5
|
+
* This module provides a browser-compatible interface for proof generation
|
|
6
|
+
* using the WASM-compiled Rust backend.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.initWasm = initWasm;
|
|
43
|
+
exports.generateProofWasm = generateProofWasm;
|
|
44
|
+
exports.isBrowser = isBrowser;
|
|
45
|
+
let wasmModule = null;
|
|
46
|
+
/**
|
|
47
|
+
* Initialize WASM module (browser only)
|
|
48
|
+
*/
|
|
49
|
+
async function initWasm() {
|
|
50
|
+
if (typeof globalThis.window === 'undefined') {
|
|
51
|
+
throw new Error('WASM loader is for browser use only. Use Node.js bindings in Node environment.');
|
|
52
|
+
}
|
|
53
|
+
if (wasmModule) {
|
|
54
|
+
return; // Already initialized
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
// Dynamic import to avoid bundler issues
|
|
58
|
+
// @ts-ignore - WASM module generated at build time
|
|
59
|
+
const wasm = await Promise.resolve().then(() => __importStar(require('../pkg/orbinum_proof_generator')));
|
|
60
|
+
await wasm.default(); // Initialize WASM
|
|
61
|
+
wasm.init_panic_hook(); // Set panic hook for better errors
|
|
62
|
+
wasmModule = wasm;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new Error(`Failed to initialize WASM module: ${error}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate proof using WASM (browser only)
|
|
70
|
+
*/
|
|
71
|
+
async function generateProofWasm(circuitType, witnessJson, provingKeyBytes) {
|
|
72
|
+
if (!wasmModule) {
|
|
73
|
+
await initWasm();
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const resultJson = wasmModule.generate_proof_wasm(circuitType, witnessJson, provingKeyBytes);
|
|
77
|
+
return JSON.parse(resultJson);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`WASM proof generation failed: ${error}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if running in browser environment
|
|
85
|
+
*/
|
|
86
|
+
function isBrowser() {
|
|
87
|
+
return (typeof globalThis.window !== 'undefined' &&
|
|
88
|
+
typeof globalThis.document !== 'undefined');
|
|
89
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Witness Generation Module
|
|
3
|
+
*
|
|
4
|
+
* Uses snarkjs to calculate witness from circuit inputs
|
|
5
|
+
*/
|
|
6
|
+
import { CircuitInputs, WitnessData } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Calculate witness using snarkjs
|
|
9
|
+
*
|
|
10
|
+
* @param inputs - Circuit inputs (key-value pairs)
|
|
11
|
+
* @param wasmPath - Path to circuit WASM file
|
|
12
|
+
* @returns Witness data as hex-encoded strings (little-endian)
|
|
13
|
+
*/
|
|
14
|
+
export declare function calculateWitness(inputs: CircuitInputs, wasmPath: string): Promise<WitnessData>;
|
|
15
|
+
/**
|
|
16
|
+
* Calculate witness and save to file (for debugging)
|
|
17
|
+
*
|
|
18
|
+
* @param inputs - Circuit inputs
|
|
19
|
+
* @param wasmPath - Path to circuit WASM
|
|
20
|
+
* @param outputPath - Where to save witness JSON
|
|
21
|
+
*/
|
|
22
|
+
export declare function calculateWitnessToFile(inputs: CircuitInputs, wasmPath: string, outputPath: string): Promise<WitnessData>;
|
package/dist/witness.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Witness Generation Module
|
|
4
|
+
*
|
|
5
|
+
* Uses snarkjs to calculate witness from circuit inputs
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.calculateWitness = calculateWitness;
|
|
42
|
+
exports.calculateWitnessToFile = calculateWitnessToFile;
|
|
43
|
+
const snarkjs = __importStar(require("snarkjs"));
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const utils_1 = require("./utils");
|
|
46
|
+
/**
|
|
47
|
+
* Calculate witness using snarkjs
|
|
48
|
+
*
|
|
49
|
+
* @param inputs - Circuit inputs (key-value pairs)
|
|
50
|
+
* @param wasmPath - Path to circuit WASM file
|
|
51
|
+
* @returns Witness data as hex-encoded strings (little-endian)
|
|
52
|
+
*/
|
|
53
|
+
async function calculateWitness(inputs, wasmPath) {
|
|
54
|
+
// Temporary path for witness file
|
|
55
|
+
const wtnsPath = `/tmp/witness_${Date.now()}.wtns`;
|
|
56
|
+
try {
|
|
57
|
+
// Step 1: Calculate witness using snarkjs
|
|
58
|
+
await snarkjs.wtns.calculate(inputs, wasmPath, wtnsPath);
|
|
59
|
+
// Step 2: Export witness to JSON
|
|
60
|
+
const witnessArray = await snarkjs.wtns.exportJson(wtnsPath);
|
|
61
|
+
// Step 3: Convert to hex little-endian format
|
|
62
|
+
const witnessHex = (0, utils_1.witnessToHexLE)(witnessArray.map((w) => BigInt(w)));
|
|
63
|
+
return {
|
|
64
|
+
witness: witnessHex,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
// Cleanup temporary file
|
|
69
|
+
if (fs.existsSync(wtnsPath)) {
|
|
70
|
+
fs.unlinkSync(wtnsPath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Calculate witness and save to file (for debugging)
|
|
76
|
+
*
|
|
77
|
+
* @param inputs - Circuit inputs
|
|
78
|
+
* @param wasmPath - Path to circuit WASM
|
|
79
|
+
* @param outputPath - Where to save witness JSON
|
|
80
|
+
*/
|
|
81
|
+
async function calculateWitnessToFile(inputs, wasmPath, outputPath) {
|
|
82
|
+
const witnessData = await calculateWitness(inputs, wasmPath);
|
|
83
|
+
fs.writeFileSync(outputPath, JSON.stringify(witnessData, null, 2));
|
|
84
|
+
return witnessData;
|
|
85
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orbinum/proof-generator",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "High-performance ZK-SNARK proof generator for Orbinum privacy protocol. Hybrid TypeScript/Rust/WASM library combining snarkjs (witness calculation) with arkworks (proof generation) to produce 128-byte compressed Groth16 proofs compatible with Substrate runtime.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"browser": "pkg/orbinum_proof_generator.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"node": "./dist/index.js",
|
|
11
|
+
"browser": "./pkg/orbinum_proof_generator.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"pkg",
|
|
18
|
+
"circuits",
|
|
19
|
+
"scripts/download-artifacts.js"
|
|
20
|
+
],
|
|
21
|
+
"author": "Orbinum",
|
|
22
|
+
"license": "GPL-3.0-or-later OR Apache-2.0",
|
|
23
|
+
"repository": {
|
|
24
|
+
"url": "https://github.com/orbinum/proof-generator"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"postinstall": "tsx scripts/download-artifacts.ts || true",
|
|
28
|
+
"build": "npm run build:rust && npm run build:wasm && npm run build:ts",
|
|
29
|
+
"build:rust": "cd rust && cargo build --release",
|
|
30
|
+
"build:wasm": "cd rust && wasm-pack build --target web --out-dir ../pkg --features wasm",
|
|
31
|
+
"build:ts": "tsc",
|
|
32
|
+
"test": "jest",
|
|
33
|
+
"test:watch": "jest --watch",
|
|
34
|
+
"test:verbose": "jest --verbose",
|
|
35
|
+
"test:unshield": "jest unshield",
|
|
36
|
+
"test:transfer": "jest transfer",
|
|
37
|
+
"test:disclosure": "jest disclosure",
|
|
38
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
39
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
40
|
+
"lint": "tsc --noEmit",
|
|
41
|
+
"prepare": "husky",
|
|
42
|
+
"prepublishOnly": "npm run build:ts && npm run lint && npm test",
|
|
43
|
+
"clean": "rm -rf dist pkg circuits node_modules && cd rust && cargo clean",
|
|
44
|
+
"release": "npm version patch && git push --follow-tags",
|
|
45
|
+
"release:minor": "npm version minor && git push --follow-tags",
|
|
46
|
+
"release:major": "npm version major && git push --follow-tags"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"zksnark",
|
|
50
|
+
"groth16",
|
|
51
|
+
"circom",
|
|
52
|
+
"snarkjs",
|
|
53
|
+
"arkworks",
|
|
54
|
+
"privacy",
|
|
55
|
+
"zk-proof",
|
|
56
|
+
"orbinum"
|
|
57
|
+
],
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"snarkjs": "0.7.5"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/circomlibjs": "0.1.6",
|
|
63
|
+
"@types/jest": "29.5.14",
|
|
64
|
+
"@types/node": "22.10.5",
|
|
65
|
+
"@types/snarkjs": "0.7.9",
|
|
66
|
+
"@types/tar": "6.1.13",
|
|
67
|
+
"circomlibjs": "0.1.7",
|
|
68
|
+
"husky": "9.1.7",
|
|
69
|
+
"jest": "29.7.0",
|
|
70
|
+
"lint-staged": "15.2.11",
|
|
71
|
+
"prettier": "3.4.2",
|
|
72
|
+
"ts-jest": "29.2.5",
|
|
73
|
+
"tsx": "^4.21.0",
|
|
74
|
+
"typescript": "5.7.3",
|
|
75
|
+
"wasm-pack": "0.14.0"
|
|
76
|
+
},
|
|
77
|
+
"lint-staged": {
|
|
78
|
+
"*.ts": [
|
|
79
|
+
"prettier --write",
|
|
80
|
+
"tsc --noEmit"
|
|
81
|
+
],
|
|
82
|
+
"*.{json,md}": [
|
|
83
|
+
"prettier --write"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"engines": {
|
|
87
|
+
"node": ">=22.0.0"
|
|
88
|
+
}
|
|
89
|
+
}
|