@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 +0 -2
- package/android/libs/arm64-v8a/libzkap_uniffi_bindings.so +0 -0
- package/android/libs/armeabi-v7a/libzkap_uniffi_bindings.so +0 -0
- package/android/libs/x86_64/libzkap_uniffi_bindings.so +0 -0
- package/android/src/main/java/uniffi/zkap_uniffi_bindings/zkap_uniffi_bindings.kt +5 -5
- package/ios/ZkapSdkModule.podspec +7 -6
- package/ios/ZkapSdkModule.swift +2 -0
- package/ios/ZkapZkp.xcframework/Info.plist +7 -7
- package/ios/ZkapZkp.xcframework/ios-arm64/libzkap_uniffi_bindings.a +0 -0
- package/ios/ZkapZkp.xcframework/ios-arm64_x86_64-simulator/{libzkap_uniffi_bindings_sim.a → libzkap_uniffi_bindings.a} +0 -0
- package/package.json +2 -3
- package/src/index.ts +1 -2
- package/src/artifact-manager.ts +0 -231
package/README.md
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
1326
|
+
|
|
1327
|
+
val errorMessage: kotlin.String
|
|
1328
1328
|
) : ZkapException() {
|
|
1329
1329
|
override val message
|
|
1330
|
-
get() = "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
|
|
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
|
|
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 = '
|
|
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 = '
|
|
19
|
+
s.vendored_frameworks = 'ZkapZkp.xcframework'
|
|
20
20
|
|
|
21
|
-
# Expose C headers for the Rust FFI symbols
|
|
22
|
-
s.preserve_paths = '
|
|
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
|
package/ios/ZkapSdkModule.swift
CHANGED
|
@@ -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-
|
|
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>
|
|
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-
|
|
32
|
+
<string>ios-arm64</string>
|
|
30
33
|
<key>LibraryPath</key>
|
|
31
|
-
<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>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baerae/zkap-zkp-react-native",
|
|
3
|
-
"version": "0.1.
|
|
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
|
|
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';
|
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
|
-
}
|