@baerae/zkap-zkp-react-native 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -11,8 +11,6 @@ npm install @baerae/zkap-zkp-react-native
11
11
  Requires Expo New Architecture and the following peer dependencies:
12
12
 
13
13
  - `expo`
14
- - `expo-crypto`
15
- - `expo-file-system`
16
14
  - `expo-modules-core`
17
15
  - `react-native`
18
16
 
@@ -1323,11 +1323,11 @@ public object FfiConverterTypeZkapSecret: FfiConverterRustBuffer<ZkapSecret> {
1323
1323
  sealed class ZkapException: kotlin.Exception() {
1324
1324
 
1325
1325
  class ApplicationException(
1326
-
1327
- val `message`: kotlin.String
1326
+
1327
+ val errorMessage: kotlin.String
1328
1328
  ) : ZkapException() {
1329
1329
  override val message
1330
- get() = "message=${ `message` }"
1330
+ get() = "message=${ errorMessage }"
1331
1331
  }
1332
1332
 
1333
1333
 
@@ -1361,7 +1361,7 @@ public object FfiConverterTypeZkapError : FfiConverterRustBuffer<ZkapException>
1361
1361
  is ZkapException.ApplicationException -> (
1362
1362
  // Add the size for the Int that specifies the variant plus the size needed for all fields
1363
1363
  4UL
1364
- + FfiConverterString.allocationSize(value.`message`)
1364
+ + FfiConverterString.allocationSize(value.errorMessage)
1365
1365
  )
1366
1366
  }
1367
1367
  }
@@ -1370,7 +1370,7 @@ public object FfiConverterTypeZkapError : FfiConverterRustBuffer<ZkapException>
1370
1370
  when(value) {
1371
1371
  is ZkapException.ApplicationException -> {
1372
1372
  buf.putInt(1)
1373
- FfiConverterString.write(value.`message`, buf)
1373
+ FfiConverterString.write(value.errorMessage, buf)
1374
1374
  Unit
1375
1375
  }
1376
1376
  }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
@@ -13,15 +13,14 @@ Pod::Spec.new do |s|
13
13
  s.swift_version = '5.9'
14
14
  s.source = { git: package['repository']['url'] }
15
15
 
16
- s.source_files = 'ios/**/*.{swift,h,m}'
16
+ s.source_files = ['ZkapSdkModule.swift', 'uniffi/**/*.{swift,h,m}']
17
17
 
18
18
  # Pre-built Rust static libraries (generated by CI build-react-native.yml)
19
- s.vendored_frameworks = 'ios/ZkapZkp.xcframework'
19
+ s.vendored_frameworks = 'ZkapZkp.xcframework'
20
20
 
21
- # Expose C headers for the Rust FFI symbols
22
- s.preserve_paths = 'ios/include/*.h'
21
+ # Expose C headers for the Rust FFI symbols (UniFFI-generated module)
22
+ s.preserve_paths = 'uniffi/*.h'
23
23
  s.xcconfig = {
24
- 'HEADER_SEARCH_PATHS' => '$(PODS_TARGET_SRCROOT)/ios/include',
25
24
  'OTHER_LDFLAGS' => '-lc++'
26
25
  }
27
26
 
@@ -29,6 +28,8 @@ Pod::Spec.new do |s|
29
28
 
30
29
  s.pod_target_xcconfig = {
31
30
  'DEFINES_MODULE' => 'YES',
32
- 'SWIFT_COMPILATION_MODE' => 'wholemodule'
31
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule',
32
+ 'HEADER_SEARCH_PATHS' => '$(PODS_TARGET_SRCROOT)/uniffi',
33
+ 'OTHER_CFLAGS' => '-fmodule-map-file=$(PODS_TARGET_SRCROOT)/uniffi/zkap_uniffi_bindingsFFI.modulemap'
33
34
  }
34
35
  end
@@ -119,6 +119,7 @@ public class ZkapSdkModule: Module {
119
119
  let outData = try JSONSerialization.data(withJSONObject: output)
120
120
  return String(data: outData, encoding: .utf8)!
121
121
  }
122
+
122
123
  }
123
124
  }
124
125
 
@@ -143,3 +144,4 @@ private func parseConfig(_ d: [String: Any]) -> ZkapCircuitConfig {
143
144
  forbiddenString: d["forbidden_string"] as! String
144
145
  )
145
146
  }
147
+
@@ -10,34 +10,34 @@
10
10
  <key>HeadersPath</key>
11
11
  <string>Headers</string>
12
12
  <key>LibraryIdentifier</key>
13
- <string>ios-arm64</string>
13
+ <string>ios-arm64_x86_64-simulator</string>
14
14
  <key>LibraryPath</key>
15
15
  <string>libzkap_uniffi_bindings.a</string>
16
16
  <key>SupportedArchitectures</key>
17
17
  <array>
18
18
  <string>arm64</string>
19
+ <string>x86_64</string>
19
20
  </array>
20
21
  <key>SupportedPlatform</key>
21
22
  <string>ios</string>
23
+ <key>SupportedPlatformVariant</key>
24
+ <string>simulator</string>
22
25
  </dict>
23
26
  <dict>
24
27
  <key>BinaryPath</key>
25
- <string>libzkap_uniffi_bindings_sim.a</string>
28
+ <string>libzkap_uniffi_bindings.a</string>
26
29
  <key>HeadersPath</key>
27
30
  <string>Headers</string>
28
31
  <key>LibraryIdentifier</key>
29
- <string>ios-arm64_x86_64-simulator</string>
32
+ <string>ios-arm64</string>
30
33
  <key>LibraryPath</key>
31
- <string>libzkap_uniffi_bindings_sim.a</string>
34
+ <string>libzkap_uniffi_bindings.a</string>
32
35
  <key>SupportedArchitectures</key>
33
36
  <array>
34
37
  <string>arm64</string>
35
- <string>x86_64</string>
36
38
  </array>
37
39
  <key>SupportedPlatform</key>
38
40
  <string>ios</string>
39
- <key>SupportedPlatformVariant</key>
40
- <string>simulator</string>
41
41
  </dict>
42
42
  </array>
43
43
  <key>CFBundlePackageType</key>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baerae/zkap-zkp-react-native",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "React Native SDK for zkap-zkp — on-device ZK proving with Groth16 (BN254) and Poseidon hash. Requires Expo New Architecture.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -40,11 +40,10 @@
40
40
  },
41
41
  "homepage": "https://github.com/baerae-zkap/zkap-zkp",
42
42
  "bugs": "https://github.com/baerae-zkap/zkap-zkp/issues",
43
+ "author": "baerae-zkap",
43
44
  "license": "MIT OR Apache-2.0",
44
45
  "peerDependencies": {
45
46
  "expo": ">=50.0.0",
46
- "expo-crypto": ">=13.0.0",
47
- "expo-file-system": ">=17.0.0",
48
47
  "expo-modules-core": ">=1.12.0",
49
48
  "react-native": ">=0.73.0"
50
49
  },
package/src/index.ts CHANGED
@@ -114,7 +114,7 @@ export async function generateLeafHash(
114
114
 
115
115
  /**
116
116
  * Generate Groth16 proofs (on-device proving).
117
- * Requires PK to be downloaded and cached via initProveArtifacts().
117
+ * Requires a proving key file on disk.
118
118
  */
119
119
  export async function prove(
120
120
  config: CircuitConfig,
@@ -152,4 +152,3 @@ export function verify(): never {
152
152
  );
153
153
  }
154
154
 
155
- export { initProveArtifacts } from './artifact-manager';
@@ -1,231 +0,0 @@
1
- /**
2
- * artifact-manager — MVP implementation
3
- *
4
- * Included: download, cache path management, SHA256 verification,
5
- * atomic rename, max 3 retries, progress callback
6
- * Excluded: HTTP Range resume, background download, advanced cache cleanup
7
- */
8
-
9
- import * as FileSystem from 'expo-file-system';
10
- import * as Crypto from 'expo-crypto';
11
-
12
- // ──────────────────────────────────────────────────────────────────
13
- // Types
14
- // ──────────────────────────────────────────────────────────────────
15
-
16
- export interface ArtifactManifest {
17
- version: string;
18
- circuit: string;
19
- artifacts: {
20
- proving_key: {
21
- url: string;
22
- sha256: string;
23
- size_bytes: number;
24
- compressed_size_bytes?: number;
25
- };
26
- };
27
- min_sdk_version: string;
28
- max_sdk_version: string;
29
- }
30
-
31
- export interface InitProveArtifactsOptions {
32
- /** URL of the manifest.json that describes the proving key location */
33
- manifestUrl: string;
34
- /**
35
- * Local directory for caching the proving key.
36
- * Defaults to `<FileSystem.cacheDirectory>/zkap/`
37
- */
38
- cacheDir?: string;
39
- /**
40
- * Called periodically during download.
41
- * `downloaded` and `total` are in bytes.
42
- */
43
- onProgress?: (downloaded: number, total: number) => void;
44
- }
45
-
46
- // ──────────────────────────────────────────────────────────────────
47
- // Constants
48
- // ──────────────────────────────────────────────────────────────────
49
-
50
- const MAX_RETRIES = 3;
51
- const RETRY_BASE_DELAY_MS = 1000;
52
-
53
- // ──────────────────────────────────────────────────────────────────
54
- // Public API
55
- // ──────────────────────────────────────────────────────────────────
56
-
57
- /**
58
- * Download (if not cached) and verify the Groth16 proving key.
59
- *
60
- * Returns the local file path to the cached proving key,
61
- * which should be passed to `prove()` as `request.pk_path`.
62
- *
63
- * @throws if download fails after 3 retries, or SHA256 verification fails
64
- */
65
- export async function initProveArtifacts(
66
- options: InitProveArtifactsOptions
67
- ): Promise<string> {
68
- const cacheDir =
69
- (options.cacheDir ?? `${FileSystem.cacheDirectory}zkap/`)
70
- .replace(/^file:\/\//, '');
71
-
72
- // Ensure cache directory exists
73
- await ensureDir(cacheDir);
74
-
75
- // 1. Download manifest
76
- const manifest = await fetchManifest(options.manifestUrl);
77
-
78
- // 2. Determine cache file path (version-namespaced to avoid stale cache)
79
- const pkInfo = manifest.artifacts.proving_key;
80
- const fileName = `zkap-${manifest.version}-pk.bin`;
81
- const pkCachePath = `${cacheDir}${fileName}`;
82
- const pkTmpPath = `${pkCachePath}.tmp`;
83
-
84
- // 3. Check if already cached and valid
85
- const fileInfo = await FileSystem.getInfoAsync(pkCachePath);
86
- if (fileInfo.exists) {
87
- const valid = await verifySha256(pkCachePath, pkInfo.sha256);
88
- if (valid) {
89
- return pkCachePath;
90
- }
91
- // Cache is corrupted — delete and re-download
92
- await FileSystem.deleteAsync(pkCachePath, { idempotent: true });
93
- }
94
-
95
- // 4. Download with retries
96
- await downloadWithRetry({
97
- url: pkInfo.url,
98
- tmpPath: pkTmpPath,
99
- totalBytes: pkInfo.size_bytes,
100
- onProgress: options.onProgress,
101
- maxRetries: MAX_RETRIES,
102
- });
103
-
104
- // 5. Verify SHA256
105
- const valid = await verifySha256(pkTmpPath, pkInfo.sha256);
106
- if (!valid) {
107
- await FileSystem.deleteAsync(pkTmpPath, { idempotent: true });
108
- throw new Error(
109
- '[zkap/artifact-manager] SHA256 verification failed. ' +
110
- 'The downloaded file may be corrupted. Please try again.'
111
- );
112
- }
113
-
114
- // 6. Atomic rename: tmp → final cache path
115
- await FileSystem.moveAsync({ from: pkTmpPath, to: pkCachePath });
116
-
117
- return pkCachePath;
118
- }
119
-
120
- // ──────────────────────────────────────────────────────────────────
121
- // Internal helpers
122
- // ──────────────────────────────────────────────────────────────────
123
-
124
- async function ensureDir(dir: string): Promise<void> {
125
- const info = await FileSystem.getInfoAsync(dir);
126
- if (!info.exists) {
127
- await FileSystem.makeDirectoryAsync(dir, { intermediates: true });
128
- }
129
- }
130
-
131
- async function fetchManifest(url: string): Promise<ArtifactManifest> {
132
- const response = await fetch(url);
133
- if (!response.ok) {
134
- throw new Error(
135
- `[zkap/artifact-manager] Failed to fetch manifest: HTTP ${response.status}`
136
- );
137
- }
138
- return response.json() as Promise<ArtifactManifest>;
139
- }
140
-
141
- interface DownloadOptions {
142
- url: string;
143
- tmpPath: string;
144
- totalBytes: number;
145
- onProgress?: (downloaded: number, total: number) => void;
146
- maxRetries: number;
147
- }
148
-
149
- async function downloadWithRetry(opts: DownloadOptions): Promise<void> {
150
- let lastError: Error | null = null;
151
-
152
- for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
153
- try {
154
- // Clean up any partial download from a previous attempt
155
- await FileSystem.deleteAsync(opts.tmpPath, { idempotent: true });
156
-
157
- const downloadResumable = FileSystem.createDownloadResumable(
158
- opts.url,
159
- opts.tmpPath,
160
- {},
161
- (progress) => {
162
- if (opts.onProgress) {
163
- opts.onProgress(
164
- progress.totalBytesWritten,
165
- progress.totalBytesExpectedToWrite > 0
166
- ? progress.totalBytesExpectedToWrite
167
- : opts.totalBytes
168
- );
169
- }
170
- }
171
- );
172
-
173
- const result = await downloadResumable.downloadAsync();
174
- if (!result || result.status !== 200) {
175
- throw new Error(
176
- `[zkap/artifact-manager] Download failed with status ${result?.status ?? 'unknown'}`
177
- );
178
- }
179
-
180
- return; // Success
181
- } catch (err) {
182
- lastError = err instanceof Error ? err : new Error(String(err));
183
- if (attempt < opts.maxRetries) {
184
- const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
185
- await sleep(delay);
186
- }
187
- }
188
- }
189
-
190
- throw new Error(
191
- `[zkap/artifact-manager] Download failed after ${opts.maxRetries} attempts. ` +
192
- `Last error: ${lastError?.message ?? 'unknown'}`
193
- );
194
- }
195
-
196
- async function verifySha256(filePath: string, expectedHex: string): Promise<boolean> {
197
- try {
198
- // Read file as base64, convert to raw binary Uint8Array, then SHA256 the binary.
199
- // NOTE: digestStringAsync hashes the string's UTF-8 bytes, not the file's binary
200
- // content. We must use Crypto.digest(algorithm, Uint8Array) for correct results.
201
- const base64 = await FileSystem.readAsStringAsync(filePath, {
202
- encoding: FileSystem.EncodingType.Base64,
203
- });
204
-
205
- // base64 → raw binary bytes
206
- const binaryStr = atob(base64);
207
- const bytes = new Uint8Array(binaryStr.length);
208
- for (let i = 0; i < binaryStr.length; i++) {
209
- bytes[i] = binaryStr.charCodeAt(i);
210
- }
211
-
212
- // Hash the raw binary
213
- const digestBuffer = await Crypto.digest(
214
- Crypto.CryptoDigestAlgorithm.SHA256,
215
- bytes
216
- );
217
-
218
- // ArrayBuffer → hex string
219
- const digestHex = Array.from(new Uint8Array(digestBuffer))
220
- .map((b) => b.toString(16).padStart(2, '0'))
221
- .join('');
222
-
223
- return digestHex.toLowerCase() === expectedHex.toLowerCase();
224
- } catch {
225
- return false;
226
- }
227
- }
228
-
229
- function sleep(ms: number): Promise<void> {
230
- return new Promise((resolve) => setTimeout(resolve, ms));
231
- }