Skip to content

Commit a92b819

Browse files
committed
add unified WASM
1 parent e3987bf commit a92b819

File tree

10 files changed

+318
-174
lines changed

10 files changed

+318
-174
lines changed

confidential-assets/README.md

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,82 @@
1-
# Test
1+
# Confidential Assets SDK
2+
3+
## WASM Dependencies
4+
5+
This package uses a unified WebAssembly module for cryptographic operations:
6+
- **Discrete log solver**: TBSGS-k32 algorithm for decryption (~512 KiB table)
7+
- **Range proofs**: Bulletproofs for range proof generation/verification
8+
9+
### How WASM Loading Works
10+
11+
The WASM binary is **not bundled** with the SDK. Instead, it is loaded dynamically at runtime when needed. This is intentional:
12+
13+
**Why not bundle the WASM?**
14+
- The `.wasm` binary is large (~774 KiB for the unified module)
15+
- Bundling would bloat every app using the SDK, even if they never use confidential assets
16+
- WASM binaries don't tree-shake - you'd pay the full size cost even if the feature is unused
17+
18+
**What the npm dependency provides:**
19+
- TypeScript type definitions
20+
- JavaScript glue code (thin wrappers that call into WASM)
21+
- These are small and get bundled normally with the SDK
22+
23+
**What gets loaded at runtime:**
24+
- The actual `.wasm` binary file
25+
- Loaded via `fetch()` + `WebAssembly.instantiate()` only when `initializeWasm()`, `initializeSolver()`, or range proof functions are called
26+
- **Single initialization**: Both discrete log and range proof functionality share the same WASM module, so it only needs to be loaded once
27+
28+
**Environment-specific loading:**
29+
30+
In **browser environments**, WASM is fetched from unpkg.com CDN.
31+
32+
In **Node.js environments** (e.g., running tests), the code automatically detects Node.js and loads WASM from local `node_modules`. This avoids network requests and ensures tests work offline.
33+
34+
### WASM Initialization
35+
36+
The SDK provides unified WASM initialization:
37+
38+
```typescript
39+
import { initializeWasm, isWasmInitialized } from "@aptos-labs/confidential-assets";
40+
41+
// Initialize once - shared between discrete log and range proofs
42+
await initializeWasm();
43+
44+
// Check if initialized
45+
if (isWasmInitialized()) {
46+
// Both discrete log and range proof functions are ready
47+
}
48+
```
49+
50+
For convenience, the SDK auto-initializes when you call any function that needs WASM. Manual initialization is only needed if you want to control when the WASM download happens.
51+
52+
### Setting Up Local WASM for Development
53+
54+
If you want to use locally-built WASM bindings (e.g., for development or testing changes):
55+
56+
1. Clone and build the WASM bindings:
57+
```bash
58+
cd ~/repos
59+
git clone https://github.com/aptos-labs/confidential-asset-wasm-bindings
60+
cd confidential-asset-wasm-bindings
61+
./scripts/build-all.sh
62+
```
63+
64+
2. Update `package.json` to use the local path:
65+
```json
66+
"@aptos-labs/confidential-asset-wasm-bindings": "file:../../confidential-asset-wasm-bindings/aptos-confidential-asset-wasm-bindings"
67+
```
68+
69+
3. Install dependencies:
70+
```bash
71+
# Use --force if you've made some local changes to the DL algorithm; otherwise the version remains the same and this does nothing
72+
pnpm install
73+
```
74+
75+
Now tests will use your locally-built WASM.
76+
77+
---
78+
79+
# Testing
280

381
To test against a modified `aptos-core` repo:
482

@@ -20,8 +98,14 @@ pnpm test
2098

2199
## Useful tests to know about
22100

23-
### Decryption times
101+
### Discrete log / decryption benchmarks
24102

103+
```bash
104+
pnpm jest tests/units/discrete-log.test.ts
25105
```
26-
pnpm jest tests/units/kangaroo-decryption.test.ts
106+
107+
### Range proof tests
108+
109+
```bash
110+
pnpm jest tests/units/confidentialProofs.test.ts
27111
```

confidential-assets/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"publish-zeta": "pnpm build && npm publish --tag zeta"
4444
},
4545
"dependencies": {
46-
"@aptos-labs/confidential-asset-wasm-bindings": "^0.0.2",
46+
"@aptos-labs/confidential-asset-wasm-bindings": "^0.0.3",
4747
"@aptos-labs/ts-sdk": "^5.0.0",
4848
"@noble/curves": "^1.6.0",
4949
"@noble/hashes": "^1.5.0"

confidential-assets/pnpm-lock.yaml

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

confidential-assets/src/crypto/bsgs.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -195,25 +195,3 @@ export class BsgsSolver {
195195
this.initPromise = undefined;
196196
}
197197
}
198-
199-
/**
200-
* Creates a decryption function using BSGS that can be passed to
201-
* TwistedElGamal.setDecryptionFn().
202-
*
203-
* @param bitWidths - Bit widths to support (e.g., [16, 32])
204-
* @returns A decryption function compatible with TwistedElGamal
205-
*/
206-
export async function createBsgsDecryptionFn(
207-
bitWidths: number[],
208-
): Promise<(point: Uint8Array) => Promise<bigint>> {
209-
const solver = new BsgsSolver();
210-
await solver.initialize(bitWidths);
211-
212-
return async (point: Uint8Array): Promise<bigint> => {
213-
// Check for zero point
214-
const isZero = point.every((b) => b === 0);
215-
if (isZero) return 0n;
216-
217-
return solver.solve(point);
218-
};
219-
}

confidential-assets/src/crypto/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from "./twistedEd25519";
22
export * from "./twistedElGamal";
33
export * from "./bsgs";
44
export * from "./rangeProof";
5+
export { initializeWasm, isWasmInitialized, ensureWasmInitialized } from "./wasmLoader";
56
export * from "./chunkedAmount";
67
export * from "./encryptedAmount";
78
export * from "./confidentialKeyRotation";

confidential-assets/src/crypto/rangeProof.ts

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
// Copyright © Aptos Foundation
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import initWasm, {
5-
range_proof as rangeProof,
6-
verify_proof as verifyProof,
7-
batch_range_proof as batchRangeProof,
8-
batch_verify_proof as batchVerifyProof,
9-
} from "@aptos-labs/confidential-asset-wasm-bindings/range-proofs";
10-
11-
const RANGE_PROOF_WASM_URL =
12-
"https://unpkg.com/@aptos-labs/confidential-asset-wasm-bindings@0.0.2/range-proofs/aptos_rp_wasm_bg.wasm";
4+
import {
5+
ensureWasmInitialized,
6+
initializeWasm,
7+
rangeProofWasm,
8+
verifyProofWasm,
9+
batchRangeProofWasm,
10+
batchVerifyProofWasm,
11+
} from "./wasmLoader";
12+
13+
/**
14+
* Initialize range proof WASM module.
15+
* @param wasmSource - Optional WASM source: URL string, or Buffer/ArrayBuffer for Node.js
16+
* @deprecated Use initializeWasm() from wasmLoader instead for unified initialization
17+
*/
18+
export async function initializeRangeProofWasm(wasmSource?: string | BufferSource) {
19+
await initializeWasm(wasmSource);
20+
}
1321

1422
export interface RangeProofInputs {
1523
v: bigint;
@@ -54,9 +62,9 @@ export class RangeProofExecutor {
5462
* @param opts.bits Bits size of value to create the range proof
5563
*/
5664
static async generateRangeZKP(opts: RangeProofInputs): Promise<{ proof: Uint8Array; commitment: Uint8Array }> {
57-
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL });
65+
await ensureWasmInitialized();
5866

59-
const proof = rangeProof(opts.v, opts.r, opts.valBase, opts.randBase, opts.bits ?? 32);
67+
const proof = rangeProofWasm(opts.v, opts.r, opts.valBase, opts.randBase, opts.bits ?? 32);
6068

6169
return {
6270
proof: proof.proof(),
@@ -74,9 +82,9 @@ export class RangeProofExecutor {
7482
* @param opts.bits Bits size of the value for range proof
7583
*/
7684
static async verifyRangeZKP(opts: VerifyRangeProofInputs): Promise<boolean> {
77-
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL });
85+
await ensureWasmInitialized();
7886

79-
return verifyProof(opts.proof, opts.commitment, opts.valBase, opts.randBase, opts.bits ?? 32);
87+
return verifyProofWasm(opts.proof, opts.commitment, opts.valBase, opts.randBase, opts.bits ?? 32);
8088
}
8189

8290
/**
@@ -91,9 +99,15 @@ export class RangeProofExecutor {
9199
static async genBatchRangeZKP(
92100
opts: BatchRangeProofInputs,
93101
): Promise<{ proof: Uint8Array; commitments: Uint8Array[] }> {
94-
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL });
102+
await ensureWasmInitialized();
95103

96-
const proof = batchRangeProof(new BigUint64Array(opts.v), opts.rs, opts.val_base, opts.rand_base, opts.num_bits);
104+
const proof = batchRangeProofWasm(
105+
new BigUint64Array(opts.v),
106+
opts.rs,
107+
opts.val_base,
108+
opts.rand_base,
109+
opts.num_bits,
110+
);
97111

98112
return {
99113
proof: proof.proof(),
@@ -111,8 +125,8 @@ export class RangeProofExecutor {
111125
* @param opts.num_bits Bits size of values to create the range proof
112126
*/
113127
static async verifyBatchRangeZKP(opts: BatchVerifyRangeProofInputs): Promise<boolean> {
114-
await initWasm({ module_or_path: RANGE_PROOF_WASM_URL });
128+
await ensureWasmInitialized();
115129

116-
return batchVerifyProof(opts.proof, opts.comm, opts.val_base, opts.rand_base, opts.num_bits);
130+
return batchVerifyProofWasm(opts.proof, opts.comm, opts.val_base, opts.rand_base, opts.num_bits);
117131
}
118132
}

0 commit comments

Comments
 (0)