@baerae/zkap-zkp-react-native 0.1.3 → 0.1.5
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 +24 -3
- package/ZkapReactNative.podspec +42 -0
- package/android/CMakeLists.txt +81 -0
- package/android/build.gradle +111 -14
- package/android/cpp-adapter.cpp +43 -0
- package/android/libs/arm64-v8a/libzkap_uniffi_bindings.so +0 -0
- package/android/libs/x86_64/libzkap_uniffi_bindings.so +0 -0
- package/android/proguard-rules.pro +2 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/baerae/zkapreactnative/ZkapReactNativeModule.kt +51 -0
- package/android/src/main/java/com/baerae/zkapreactnative/ZkapReactNativePackage.kt +34 -0
- package/cpp/baerae-zkap-react-native.cpp +17 -0
- package/cpp/baerae-zkap-react-native.h +15 -0
- package/cpp/generated/zkap_uniffi_bindings.cpp +2154 -0
- package/cpp/generated/zkap_uniffi_bindings.hpp +66 -0
- package/ios/ZkapReactNative.h +16 -0
- package/ios/ZkapReactNative.mm +412 -0
- package/ios/ZkapZkp.xcframework/Info.plist +5 -5
- package/ios/ZkapZkp.xcframework/ios-arm64/libzkap_uniffi_bindings.a +0 -0
- package/ios/ZkapZkp.xcframework/ios-arm64_x86_64-simulator/libzkap_uniffi_bindings.a +0 -0
- package/package.json +37 -16
- package/src/NativeZkapReactNative.ts +11 -0
- package/src/generated/zkap_uniffi_bindings-ffi.ts +150 -0
- package/src/generated/zkap_uniffi_bindings.ts +1015 -0
- package/src/index.ts +250 -47
- package/ubrn.config.yaml +13 -0
- package/android/libs/armeabi-v7a/libzkap_uniffi_bindings.so +0 -0
- package/android/src/main/java/expo/modules/zkap/ZkapSdkModule.kt +0 -153
- package/android/src/main/java/uniffi/zkap_uniffi_bindings/zkap_uniffi_bindings.kt +0 -1566
- package/expo-module.config.json +0 -9
- package/ios/ZkapSdkModule.podspec +0 -35
- package/ios/ZkapSdkModule.swift +0 -147
- package/ios/uniffi/zkap_uniffi_bindings.swift +0 -1174
- package/ios/uniffi/zkap_uniffi_bindingsFFI.h +0 -567
- package/ios/uniffi/zkap_uniffi_bindingsFFI.modulemap +0 -7
- package/src/artifact-manager.ts +0 -231
package/src/artifact-manager.ts
DELETED
|
@@ -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
|
-
}
|