Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
584f26c
refactor(tests): remove Disclosure circuit tests and update to ValueP…
nol4lej May 15, 2026
199ccc7
refactor(tests): update circuit type from Disclosure to Unshield in p…
nol4lej May 15, 2026
32a1cbe
refactor(tests): remove generateDisclosureProof tests for deprecated …
nol4lej May 15, 2026
239c6d2
refactor(tests): remove Disclosure circuit tests from config test suite
nol4lej May 15, 2026
89916c2
refactor(disclosure): remove deprecated Disclosure circuit and relate…
nol4lej May 15, 2026
c6ab3bd
refactor(readme): update performance metrics and circuit names from D…
nol4lej May 15, 2026
320593c
refactor(types): replace Disclosure circuit type with ValueProof
nol4lej May 15, 2026
7b1e845
refactor(config): replace Disclosure circuit with ValueProof in expec…
nol4lej May 15, 2026
d2462ce
refactor(index): remove generateDisclosureProof and related types fro…
nol4lej May 15, 2026
0978130
refactor(usage): replace Disclosure circuit with ValueProof in docume…
nol4lej May 15, 2026
d7bacc4
refactor(docs): rename Disclosure references to ValueProof in develop…
nol4lej May 15, 2026
7fc3297
refactor(docs): update performance metrics and replace Disclosure wit…
nol4lej May 15, 2026
a766d7e
refactor(docs): update API documentation to replace Disclosure with V…
nol4lej May 15, 2026
f656f8e
refactor(changelog): update to version 3.7.0 with ValueProof circuit …
nol4lej May 15, 2026
b637fcc
refactor(tests): add ValueProof artifacts to NodeArtifactProvider tests
nol4lej May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.7.0] - 2026-05-14

### Added

- **`CircuitType.ValueProof`** (`'value_proof'`) added to `src/circuits/types.ts` — replaces the removed `Disclosure` circuit. The `ValueProof` circuit proves that a note commitment encodes exactly the declared `value` and `asset_id` without revealing the spending key or requiring a Merkle proof.
- **`src/circuits/config.ts`** — `expectedPublicSignals` case for `ValueProof`: `4` (`[commitment, value, asset_id, owner_hash]`).
- **`bench/value-proof.ts`** — Standalone benchmark script for the `value_proof` circuit. Measures snarkjs and arkworks backends over 5 post-warmup runs and prints formatted tables ready to paste into docs.
- **`bench/transfer.ts`** — Standalone benchmark script for the `transfer` circuit (same structure).
- **`bench/tsconfig.json`** — TypeScript config scoped to the `bench/` directory.

### Changed

- **Performance docs updated with real measured numbers** (Apple M-series, Node.js 22, 5 runs post-warmup):
- `ValueProof` snarkjs: **91ms**; arkworks witness 24ms + serialize 3ms + prove 234ms = **262ms total**.
- `Transfer` snarkjs: **1148ms** (arkworks not yet measured — `.ark` key pending).

## [3.6.0] - 2026-05-12

### Changed
Expand Down
24 changes: 7 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,28 @@ Benchmarked on Apple M-series (Node.js, 3 runs post-warmup):

| Circuit | snarkjs backend | arkworks backend | First call overhead |
|---------|----------------|-----------------|---------------------|
| Disclosure | ~80ms | ~253ms | +1.5–2s (WASM init) |
| ValueProof | ~91ms | ~262ms | +1.5–2s (WASM init) |
| PrivateLink | ~73ms | ~234ms | +1.5–2s (WASM init) |
| Unshield | ~407ms | ~2.1s | +1.5–2s (WASM init) |
| Transfer | ~1.2s | ~7.2s | +1.5–2s (WASM init) |
| Transfer | ~1.1s | ~7.2s | +1.5–2s (WASM init) |

> **snarkjs backend** (default): uses snarkjs `fullProve` with `.zkey` proving keys — fastest option post-warmup.
>
> **arkworks backend**: uses snarkjs witness-only + arkworks WASM with `.ark` proving keys — ~3× slower for small circuits (Disclosure, PrivateLink), ~5× slower for large circuits (Unshield, Transfer). `.ark` artifacts are 2–3× smaller than `.zkey`.
> **arkworks backend**: uses snarkjs witness-only + arkworks WASM with `.ark` proving keys — ~3× slower for small circuits (ValueProof, PrivateLink), ~5× slower for large circuits (Unshield, Transfer). `.ark` artifacts are 2–3× smaller than `.zkey`.

The first proof call in a process incurs the WASM initialization overhead (~1.5–2s). All subsequent proofs skip this.

**Phase breakdown — where each backend spends its time (1 run):**

| Circuit | Backend | Load | Witness | Serialize | Prove | Compress | Total |
|---------|---------|------|---------|-----------|-------|----------|-------|
| Disclosure | snarkjs | 9ms | — | — | 78ms | — | 87ms |
| Disclosure | arkworks | 2ms | 20ms | 3ms | 228ms | — | 253ms |
| ValueProof | snarkjs | 9ms | — | — | 91ms | — | 100ms |
| ValueProof | arkworks | | 24ms | 3ms | 234ms | — | 262ms |
| PrivateLink | snarkjs | 8ms | — | — | 75ms | — | 83ms |
| PrivateLink | arkworks | 2ms | 14ms | 2ms | 216ms | — | 234ms |
| Unshield | snarkjs | 21ms | — | — | 367ms | — | 388ms |
| Unshield | arkworks | 8ms | 28ms | 26ms | 1965ms | — | 2027ms |
| Transfer | snarkjs | 54ms | — | — | 1212ms | — | 1266ms |
| Transfer | snarkjs | 54ms | — | — | 1094ms | — | 1148ms |
| Transfer | arkworks | 24ms | 94ms | 101ms | 6901ms | — | 7120ms |

> `Prove` represents 97% of total time for large circuits (Unshield, Transfer). Load, witness calculation, and serialization are negligible. For arkworks, `Prove` includes PK deserialization + `Groth16::prove` inside WASM.
Expand All @@ -88,7 +88,7 @@ The first proof call in a process incurs the WASM initialization overhead (~1.5
| ----------- | ------------------------------------------------ |
| Unshield | Withdraw from pool to public address |
| Transfer | Private-to-private transfer |
| Disclosure | Selective revelation |
| ValueProof | Prove note value without revealing full details |
| PrivateLink | Privacy-preserving cross-chain identity dispatch |

## Related Packages
Expand All @@ -97,16 +97,6 @@ The first proof call in a process incurs the WASM initialization overhead (~1.5
- [@orbinum/groth16-proofs](https://www.npmjs.com/package/@orbinum/groth16-proofs) - WASM proof generator (installed automatically)
- [orbinum/node](https://github.com/orbinum/node) - Substrate blockchain node

## Migration from v1.x

If you're upgrading from v1.x:

- ✅ No code changes required
- ✅ Artifacts now come from npm instead of GitHub releases
- ✅ Faster installation (npm cache)
- ✅ Offline-friendly
- ❌ Old `circuits/` and `groth16-proof/` directories can be deleted

## License

Dual-licensed under Apache 2.0 or GPL 3.0. See [LICENSE-APACHE2](LICENSE-APACHE2) and [LICENSE-GPL3](LICENSE-GPL3).
58 changes: 24 additions & 34 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,66 +107,55 @@ console.log(result.publicSignals);

---

### `generateDisclosureProof(value, ownerPubkey, blinding, assetId, commitment, mask, options?)`
### `generateValueProof(value, ownerPubkey, blinding, assetId, commitment, options?)`

High-level helper for the `Disclosure` circuit. Handles Poseidon key derivation
(`viewing_key = Poseidon(ownerPubkey)`) and returns human-readable revealed data.
High-level helper for the `ValueProof` circuit. Computes `owner_hash = Poseidon(ownerPubkey)`
and returns the decoded public signals alongside the proof.

**Parameters:**

```typescript
await generateDisclosureProof(
await generateValueProof(
value: bigint, // Note value (u64)
ownerPubkey: bigint, // Owner public key (BN254 scalar)
blinding: bigint, // Blinding factor
assetId: bigint, // Asset ID (u32)
commitment: bigint, // Note commitment
mask: DisclosureMask, // Which fields to disclose
options?: GenerateOptions
)
```

**`DisclosureMask`:**
**Returns:** `Promise<ValueProofOutput>`

```typescript
interface DisclosureMask {
discloseValue: boolean; // Reveal note value
discloseAssetId: boolean; // Reveal asset ID
discloseOwner: boolean; // Reveal Poseidon(ownerPubkey)
}
```

**Returns:** `Promise<DisclosureProofOutput>`

```typescript
interface DisclosureProofOutput {
proof: string; // 128-byte compressed proof (0x-prefixed)
publicSignals: string[]; // [commitment, revealed_value, revealed_asset_id, revealed_owner_hash]
revealedData: {
value?: string; // Decimal string, or undefined if not disclosed
assetId?: number; // Number, or undefined if not disclosed
ownerHash?: string; // 0x-prefixed hex, or undefined if not disclosed
interface ValueProofOutput {
proof: string; // 128-byte compressed proof (0x-prefixed)
publicSignals: string[]; // [commitment, value, asset_id, owner_hash]
decoded: {
commitment: string; // 0x-prefixed hex (32 bytes)
value: string; // Decimal string (u64)
assetId: number; // u32
ownerHash: string; // 0x-prefixed hex (32 bytes) — Poseidon(ownerPubkey)
};
}
```

**Example:**

```typescript
import { generateDisclosureProof } from '@orbinum/proof-generator';
import { generateValueProof } from '@orbinum/proof-generator';

const result = await generateDisclosureProof(
const result = await generateValueProof(
1000n, // value
ownerPubkey, // bigint
blinding, // bigint
42n, // assetId
commitment, // bigint
{ discloseValue: true, discloseAssetId: true, discloseOwner: false }
);

console.log(result.revealedData.value); // '1000'
console.log(result.revealedData.assetId); // 42
console.log(result.revealedData.ownerHash); // undefined (not disclosed)
console.log(result.decoded.value); // '1000'
console.log(result.decoded.assetId); // 42
console.log(result.decoded.ownerHash); // '0x...' (always present)
```

## Enumerations
Expand All @@ -179,7 +168,7 @@ Supported circuits:
enum CircuitType {
Unshield = 'unshield', // Withdrawal to public address
Transfer = 'transfer', // Private transfer
Disclosure = 'disclosure', // Selective revelation
ValueProof = 'value_proof', // Prove note value (commitment binding)
PrivateLink = 'private_link', // Privacy-preserving cross-chain identity dispatch
}
```
Expand All @@ -191,7 +180,7 @@ import { CircuitType } from '@orbinum/proof-generator';

await generateProof(CircuitType.Unshield, inputs);
await generateProof(CircuitType.Transfer, inputs);
await generateProof(CircuitType.Disclosure, inputs);
await generateProof(CircuitType.ValueProof, inputs);
await generateProof(CircuitType.PrivateLink, inputs);
```

Expand Down Expand Up @@ -289,7 +278,7 @@ try {
| --------------- | -------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ |
| **Unshield** | 5 | `merkle_root`, `nullifier`, `amount`, `recipient`, `asset_id`, note fields, `path_*` | Withdraw from pool to public address |
| **Transfer** | 5 | `merkle_root`, input/output nullifiers and commitments, note fields, `path_*` | Private-to-private transfer |
| **Disclosure** | 4 | `commitment`, `viewing_key`, revealed fields, disclosure masks, note fields | Selective revelation |
| **ValueProof** | 4 | `commitment`, `value`, `asset_id`, `owner_hash`, note fields | Prove note value without revealing extra state |
| **PrivateLink** | 2 | `commitment`, `call_hash_fe` | Privacy-preserving cross-chain identity dispatch |

### Output Format
Expand Down Expand Up @@ -317,8 +306,9 @@ See [docs/backends.md](backends.md) for a full benchmark analysis.
| --- | --- | --- |
| Unshield | ~1.3 s | ~7 s |
| Transfer | ~4.7 s | ~20 s |
| Disclosure | ~1.1 s | ~5 s |
| ValueProof | ~1.1 s | ~5 s |
| PrivateLink | ~0.7 s | ~3 s |
```

- **snarkjs** — default, fastest, uses `.zkey` proving keys
- **arkworks** — 2–3× smaller proof artifacts, uses `.ark` proving keys
Expand Down Expand Up @@ -423,7 +413,7 @@ Proof generation is compute-intensive. Expected times:

- **Unshield**: ~1.5s
- **Transfer**: ~3s
- **Disclosure**: ~0.8s
- **ValueProof**: ~0.8s
- **PrivateLink**: ~0.7s

For faster proofs, ensure:
Expand Down
12 changes: 6 additions & 6 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
| **Proof generation** | JavaScript (snarkjs `groth16.fullProve`) | WASM (arkworks `Groth16::prove`) |
| **Artifact size** | Larger (`.zkey`) | 2–3× smaller (`.ark`) |
| **Speed (small circuits)** | ~80ms | ~250–280ms |
| **Speed (large circuits)** | ~380ms–1.3s | ~2s–7s |
| **Speed (large circuits)** | ~380ms–1.1s | ~2s–7s |
| **Default** | ✅ Yes | No |

---
Expand All @@ -32,10 +32,10 @@ inputs → snarkjs.groth16.fullProve(inputs, wasm, zkey)

| Circuit | Load | Prove (fullProve) | Compress | Total |
|---------|------|-------------------|----------|-------|
| Disclosure | 8ms | 84ms | 1ms | ~93ms |
| ValueProof | 9ms | 82ms | 1ms | ~91ms |
| PrivateLink | 8ms | 70ms | — | ~78ms |
| Unshield | 21ms | 365ms | — | ~386ms |
| Transfer | 54ms | 1194ms | — | ~1248ms |
| Transfer | 54ms | 1094ms | — | ~1148ms |

### arkworks

Expand All @@ -53,7 +53,7 @@ The arkworks backend splits the work in two: snarkjs calculates the witness in m

| Circuit | Load | Witness | Serialize | Prove (WASM) | Total |
|---------|------|---------|-----------|--------------|-------|
| Disclosure | 1ms | 21ms | 3ms | 226ms | ~251ms |
| ValueProof | | 24ms | 3ms | 234ms | ~262ms |
| PrivateLink | 2ms | 14ms | 2ms | 214ms | ~232ms |
| Unshield | 8ms | 29ms | 27ms | 1955ms | ~2019ms |
| Transfer | 27ms | 78ms | 99ms | 7093ms | ~7297ms |
Expand All @@ -68,10 +68,10 @@ Measured on Apple M-series, Node.js ≥ 22, 3 runs post-warmup (median reported)

| Circuit | snarkjs | arkworks | Ratio |
|---------|---------|----------|-------|
| Disclosure | 80ms | 282ms | 0.28× |
| ValueProof | 91ms | 262ms | 0.35× |
| PrivateLink | 74ms | 232ms | 0.32× |
| Unshield | 380ms | 2061ms | 0.18× |
| Transfer | 1323ms | 7041ms | 0.19× |
| Transfer | 1148ms | 7041ms | 0.16× |

snarkjs is consistently **3–5× faster** than arkworks post-warmup.

Expand Down
30 changes: 15 additions & 15 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ proof-generator/
│ │ ├── snarkjs.ts runSnarkjsBackend()
│ │ └── arkworks.ts runArkworksBackend()
│ │
│ ├── disclosure/ Selective disclosure helpers
│ │ ├── index.ts generateDisclosureProof()
│ │ └── types.ts DisclosureMask, DisclosureProofOutput
│ ├── value_proof/ Value proof helpers
│ │ ├── index.ts generateValueProof()
│ │ └── types.ts ValueProofOutput
│ │
│ ├── circuits/ Circuit configuration
│ │ ├── config.ts getCircuitConfig()
Expand Down Expand Up @@ -73,8 +73,8 @@ proof-generator/
│ ├── generate/
│ │ ├── index.test.ts generateProof() — 15 tests
│ │ └── provider.test.ts resolveProvider() — 4 tests
│ ├── disclosure/
│ │ └── index.test.ts generateDisclosureProof() — 9 tests
│ ├── value_proof/
│ │ └── index.test.ts generateValueProof() — 9 tests
│ ├── errors/
│ │ └── index.test.ts Error class hierarchy — 11 tests
│ ├── circuits/
Expand Down Expand Up @@ -230,10 +230,10 @@ provider.getCircuitWasm + getCircuitProvingKey (.ark)
→ generateProofFromWitnessWasm(witness, provingKey)
```

### `src/disclosure/index.ts`
### `src/value_proof/index.ts`

Uses `circomlibjs.buildPoseidon` to compute `viewing_key = Poseidon(ownerPubkey)`, then calls
`generateProof(CircuitType.Disclosure, ...)` and maps raw signals to `DisclosureProofOutput.revealedData`.
Uses `circomlibjs.buildPoseidon` to compute `owner_hash = Poseidon(ownerPubkey)`, then calls
`generateProof(CircuitType.ValueProof, ...)` and maps raw signals to `ValueProofOutput.decoded`.

### `src/errors/index.ts`

Expand Down Expand Up @@ -285,7 +285,7 @@ generateProof()

- **snarkjs 0.7.6**: Witness calculation for all circuits; also the full prover in the `snarkjs` backend
- **@orbinum/groth16-proofs 2.1.0**: Arkworks Groth16 WASM — proof generation in `arkworks` backend, and 128-byte compression for `snarkjs` backend
- **circomlibjs 0.1.7**: Poseidon hash implementation — used exclusively in `src/disclosure/`
- **circomlibjs 0.1.7**: Poseidon hash implementation — used exclusively in `src/value_proof/`

### Provider System

Expand Down Expand Up @@ -328,9 +328,9 @@ node_modules/
│ ├── transfer.wasm
│ ├── transfer_pk.ark
│ ├── transfer_pk.zkey
│ ├── disclosure.wasm
│ ├── disclosure_pk.ark
│ ├── disclosure_pk.zkey
│ ├── value_proof.wasm
│ ├── value_proof_pk.ark
│ ├── value_proof_pk.zkey
│ ├── private_link.wasm
│ ├── private_link_pk.ark
│ └── private_link_pk.zkey
Expand Down Expand Up @@ -361,8 +361,8 @@ tests/
├── generate/
│ ├── index.test.ts (15 tests) generateProof — mocked provider & backends
│ └── provider.test.ts ( 4 tests) resolveProvider — environment detection
├── disclosure/
│ └── index.test.ts ( 9 tests) generateDisclosureProof — mocked Poseidon
├── value_proof/
│ └── index.test.ts ( 9 tests) generateValueProof — mocked Poseidon
├── errors/
│ └── index.test.ts (11 tests) Error hierarchy and .code values
├── circuits/
Expand Down Expand Up @@ -440,7 +440,7 @@ pnpm test:coverage # With coverage report
| `@vitest/coverage-v8` | `4.1.3` | Coverage reports |
| `typescript` | `6.0.2` | Compiler |
| `prettier` | `3.8.1` | Code formatting |
| `circomlibjs` | `0.1.7` | Poseidon hash (disclosure module) |
| `circomlibjs` | `0.1.7` | Poseidon hash (value_proof module) |
| `@types/node` | `25.5.2` | Node.js type definitions |
| `@types/snarkjs` | `0.7.9` | snarkjs type definitions |
| `@types/circomlibjs` | `0.1.6` | circomlibjs type definitions |
Expand Down
30 changes: 10 additions & 20 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,48 +64,38 @@ See [backends.md](./backends.md) for a full comparison of speed and artifact siz
|---------|--------------|----------------|----------|
| Unshield | `CircuitType.Unshield` | 7 | Withdraw from pool to public address |
| Transfer | `CircuitType.Transfer` | 7 | Private-to-private transfer |
| Disclosure | `CircuitType.Disclosure` | 4 | Selective field revelation to auditor |
| ValueProof | `CircuitType.ValueProof` | 4 | Prove note value and ownership without revealing the spending key |
| PrivateLink | `CircuitType.PrivateLink` | 2 | Privacy-preserving cross-chain identity |

---

## Selective Disclosure
## Value Proof

The `Disclosure` circuit has a dedicated helper that handles Poseidon key derivation and decodes the public signals into human-readable output:
The `ValueProof` circuit has a dedicated helper that computes `owner_hash = Poseidon(ownerPubkey)` and decodes the public signals into named fields:

```typescript
import { generateDisclosureProof } from '@orbinum/proof-generator';
import type { DisclosureMask } from '@orbinum/proof-generator';
import { generateValueProof } from '@orbinum/proof-generator';

const mask: DisclosureMask = {
discloseValue: true, // reveal the note value
discloseAssetId: true, // reveal the asset ID
discloseOwner: false, // keep owner private
};

const result = await generateDisclosureProof(
const result = await generateValueProof(
1000n, // value (bigint, u64)
pubkey, // ownerPubkey (bigint, BN254 scalar)
blinding, // blinding factor (bigint)
1n, // assetId (bigint, u32)
commitment, // note commitment (bigint)
mask,
);

console.log(result.proof); // "0x..." (128 bytes)
console.log(result.publicSignals); // 4 hex signals
console.log(result.revealedData);
console.log(result.decoded);
// {
// commitment: "0x...",
// value: "1000", // decimal string (present because discloseValue: true)
// assetId: 1, // number (present because discloseAssetId: true)
// ownerHash: undefined // absent because discloseOwner: false
// value: "1000", // decimal string
// assetId: 1, // number
// ownerHash: "0x..." // Poseidon(ownerPubkey)
// }
```

At least one mask field must be `true` — passing all `false` throws an error.

You can also use `generateProof(CircuitType.Disclosure, inputs)` directly if you build the circuit inputs object manually.
You can also use `generateProof(CircuitType.ValueProof, inputs)` directly if you build the circuit inputs object manually.

---

Expand Down
Loading
Loading