@baerae/zkap-zkp-react-native 0.1.4 → 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.
Files changed (34) hide show
  1. package/README.md +24 -1
  2. package/ZkapReactNative.podspec +42 -0
  3. package/android/CMakeLists.txt +81 -0
  4. package/android/build.gradle +111 -14
  5. package/android/cpp-adapter.cpp +43 -0
  6. package/android/libs/arm64-v8a/libzkap_uniffi_bindings.so +0 -0
  7. package/android/libs/x86_64/libzkap_uniffi_bindings.so +0 -0
  8. package/android/proguard-rules.pro +2 -0
  9. package/android/src/main/AndroidManifest.xml +5 -0
  10. package/android/src/main/java/com/baerae/zkapreactnative/ZkapReactNativeModule.kt +51 -0
  11. package/android/src/main/java/com/baerae/zkapreactnative/ZkapReactNativePackage.kt +34 -0
  12. package/cpp/baerae-zkap-react-native.cpp +17 -0
  13. package/cpp/baerae-zkap-react-native.h +15 -0
  14. package/cpp/generated/zkap_uniffi_bindings.cpp +2154 -0
  15. package/cpp/generated/zkap_uniffi_bindings.hpp +66 -0
  16. package/ios/ZkapReactNative.h +16 -0
  17. package/ios/ZkapReactNative.mm +412 -0
  18. package/ios/ZkapZkp.xcframework/ios-arm64/libzkap_uniffi_bindings.a +0 -0
  19. package/ios/ZkapZkp.xcframework/ios-arm64_x86_64-simulator/libzkap_uniffi_bindings.a +0 -0
  20. package/package.json +37 -14
  21. package/src/NativeZkapReactNative.ts +11 -0
  22. package/src/generated/zkap_uniffi_bindings-ffi.ts +150 -0
  23. package/src/generated/zkap_uniffi_bindings.ts +1015 -0
  24. package/src/index.ts +250 -46
  25. package/ubrn.config.yaml +13 -0
  26. package/android/libs/armeabi-v7a/libzkap_uniffi_bindings.so +0 -0
  27. package/android/src/main/java/expo/modules/zkap/ZkapSdkModule.kt +0 -153
  28. package/android/src/main/java/uniffi/zkap_uniffi_bindings/zkap_uniffi_bindings.kt +0 -1566
  29. package/expo-module.config.json +0 -9
  30. package/ios/ZkapSdkModule.podspec +0 -35
  31. package/ios/ZkapSdkModule.swift +0 -147
  32. package/ios/uniffi/zkap_uniffi_bindings.swift +0 -1174
  33. package/ios/uniffi/zkap_uniffi_bindingsFFI.h +0 -567
  34. package/ios/uniffi/zkap_uniffi_bindingsFFI.modulemap +0 -7
@@ -0,0 +1,66 @@
1
+ // This file was autogenerated by some hot garbage in the `uniffi-bindgen-react-native` crate.
2
+ // Trust me, you don't want to mess with it!
3
+ #pragma once
4
+ #include <jsi/jsi.h>
5
+ #include <iostream>
6
+ #include <map>
7
+ #include <memory>
8
+ #include <ReactCommon/CallInvoker.h>
9
+ #include "UniffiCallInvoker.h"
10
+
11
+ namespace react = facebook::react;
12
+ namespace jsi = facebook::jsi;
13
+
14
+ class NativeZkapUniffiBindings : public jsi::HostObject {
15
+ private:
16
+ // For calling back into JS from Rust.
17
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker;
18
+
19
+ protected:
20
+ std::map<std::string,jsi::Value> props;
21
+ jsi::Value cpp_uniffi_internal_fn_func_ffi__string_to_byte_length(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
22
+ jsi::Value cpp_uniffi_internal_fn_func_ffi__string_to_arraybuffer(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
23
+ jsi::Value cpp_uniffi_internal_fn_func_ffi__arraybuffer_to_string(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
24
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_generate_anchor(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
25
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_generate_aud_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
26
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_generate_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
27
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_generate_leaf_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
28
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_prepare_witness_inputs(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
29
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_prove(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
30
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_prove_from_witness_bundle_file(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
31
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_fn_func_prove_from_witness_bundles(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
32
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_generate_anchor(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
33
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_generate_aud_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
34
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_generate_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
35
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_generate_leaf_hash(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
36
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_prepare_witness_inputs(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
37
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_prove(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
38
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_prove_from_witness_bundle_file(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
39
+ jsi::Value cpp_uniffi_zkap_uniffi_bindings_checksum_func_prove_from_witness_bundles(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
40
+ jsi::Value cpp_ffi_zkap_uniffi_bindings_uniffi_contract_version(jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count);
41
+
42
+ public:
43
+ NativeZkapUniffiBindings(jsi::Runtime &rt, std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker);
44
+ virtual ~NativeZkapUniffiBindings();
45
+
46
+ /**
47
+ * The entry point into the crate.
48
+ *
49
+ * React Native must call `NativeZkapUniffiBindings.registerModule(rt, callInvoker)` before using
50
+ * the Javascript interface.
51
+ */
52
+ static void registerModule(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> callInvoker);
53
+
54
+ /**
55
+ * Some cleanup into the crate goes here.
56
+ *
57
+ * Current implementation is empty, however, this is not guaranteed to always be the case.
58
+ *
59
+ * Clients should call `NativeZkapUniffiBindings.unregisterModule(rt)` after final use where possible.
60
+ */
61
+ static void unregisterModule(jsi::Runtime &rt);
62
+
63
+ virtual jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name);
64
+ virtual void set(jsi::Runtime& rt,const jsi::PropNameID& name,const jsi::Value& value);
65
+ virtual std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt);
66
+ };
@@ -0,0 +1,16 @@
1
+ // Generated by uniffi-bindgen-react-native
2
+ #ifdef __cplusplus
3
+ #import "baerae-zkap-react-native.h"
4
+ #endif
5
+
6
+ #ifdef RCT_NEW_ARCH_ENABLED
7
+ #import "ZkapReactNativeSpec.h"
8
+
9
+ @interface ZkapReactNative : NSObject <NativeZkapReactNativeSpec>
10
+ #else
11
+ #import <React/RCTBridgeModule.h>
12
+
13
+ @interface ZkapReactNative : NSObject <RCTBridgeModule>
14
+ #endif
15
+
16
+ @end
@@ -0,0 +1,412 @@
1
+ // Generated by uniffi-bindgen-react-native
2
+ #import "ZkapReactNative.h"
3
+ #import <CommonCrypto/CommonDigest.h>
4
+ #import <WebKit/WebKit.h>
5
+ #import <stdlib.h>
6
+
7
+ static NSString *ZkapSha256Hex(NSData *data) {
8
+ unsigned char digest[CC_SHA256_DIGEST_LENGTH];
9
+ CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
10
+ NSMutableString *hex = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
11
+ for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
12
+ [hex appendFormat:@"%02x", digest[i]];
13
+ }
14
+ return hex;
15
+ }
16
+
17
+ static BOOL ZkapReadUInt64(id value, unsigned long long *out) {
18
+ if ([value isKindOfClass:NSNumber.class]) {
19
+ *out = [value unsignedLongLongValue];
20
+ return YES;
21
+ }
22
+ if (![value isKindOfClass:NSString.class]) {
23
+ return NO;
24
+ }
25
+
26
+ const char *text = [(NSString *)value UTF8String];
27
+ if (!text || text[0] == '\0') {
28
+ return NO;
29
+ }
30
+ char *end = NULL;
31
+ unsigned long long parsed = strtoull(text, &end, 10);
32
+ if (end == text || *end != '\0') {
33
+ return NO;
34
+ }
35
+ *out = parsed;
36
+ return YES;
37
+ }
38
+
39
+ static BOOL ZkapValidateBase64InputField(NSDictionary *input,
40
+ NSString *payloadKey,
41
+ NSString *lengthKey,
42
+ NSString *shaKey,
43
+ NSString **error) {
44
+ NSString *payload = [input[payloadKey] isKindOfClass:NSString.class] ? input[payloadKey] : nil;
45
+ NSString *expectedSha = [input[shaKey] isKindOfClass:NSString.class] ? input[shaKey] : nil;
46
+ unsigned long long expectedLength = 0;
47
+ if (!payload || !expectedSha || !ZkapReadUInt64(input[lengthKey], &expectedLength)) {
48
+ *error = [NSString stringWithFormat:@"invalid WKWebView witness input field %@", payloadKey];
49
+ return NO;
50
+ }
51
+
52
+ NSData *decoded = [[NSData alloc] initWithBase64EncodedString:payload options:0];
53
+ if (!decoded) {
54
+ *error = [NSString stringWithFormat:@"invalid WKWebView witness base64 field %@", payloadKey];
55
+ return NO;
56
+ }
57
+ if (decoded.length != expectedLength) {
58
+ *error = [NSString stringWithFormat:@"WKWebView witness input length mismatch for %@", payloadKey];
59
+ return NO;
60
+ }
61
+ NSString *actualSha = ZkapSha256Hex(decoded);
62
+ if ([actualSha caseInsensitiveCompare:expectedSha] != NSOrderedSame) {
63
+ *error = [NSString stringWithFormat:@"WKWebView witness input sha256 mismatch for %@", payloadKey];
64
+ return NO;
65
+ }
66
+ return YES;
67
+ }
68
+
69
+ static BOOL ZkapValidateWitnessInput(NSDictionary *input, NSString **error) {
70
+ return ZkapValidateBase64InputField(input, @"wasm_base64", @"wasm_byte_length", @"witness_gen_sha256", error) &&
71
+ ZkapValidateBase64InputField(input, @"request_json_base64", @"request_json_byte_length", @"request_json_sha256", error) &&
72
+ ZkapValidateBase64InputField(input, @"config_json_base64", @"config_json_byte_length", @"config_json_sha256", error);
73
+ }
74
+
75
+ @class ZkapWitnessRunner;
76
+
77
+ static NSMutableSet<ZkapWitnessRunner *> *ZkapActiveWitnessRunners() {
78
+ static NSMutableSet<ZkapWitnessRunner *> *runners;
79
+ static dispatch_once_t onceToken;
80
+ dispatch_once(&onceToken, ^{
81
+ runners = [NSMutableSet new];
82
+ });
83
+ return runners;
84
+ }
85
+
86
+ @interface ZkapWitnessRunner : NSObject <WKScriptMessageHandler, WKNavigationDelegate>
87
+ @property (nonatomic, copy) NSString *inputJson;
88
+ @property (nonatomic, copy) NSString *runId;
89
+ @property (nonatomic, copy) RCTPromiseResolveBlock resolve;
90
+ @property (nonatomic, copy) RCTPromiseRejectBlock reject;
91
+ @property (nonatomic, strong) WKWebView *webView;
92
+ @property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSString *> *chunks;
93
+ @property (nonatomic, assign) NSInteger totalChunks;
94
+ @property (nonatomic, assign) NSUInteger base64Length;
95
+ @property (nonatomic, assign) BOOL finished;
96
+ + (void)runWithInputJson:(NSString *)inputJson
97
+ resolve:(RCTPromiseResolveBlock)resolve
98
+ reject:(RCTPromiseRejectBlock)reject;
99
+ @end
100
+
101
+ @implementation ZkapWitnessRunner
102
+
103
+ + (void)runWithInputJson:(NSString *)inputJson
104
+ resolve:(RCTPromiseResolveBlock)resolve
105
+ reject:(RCTPromiseRejectBlock)reject
106
+ {
107
+ dispatch_async(dispatch_get_main_queue(), ^{
108
+ ZkapWitnessRunner *runner = [ZkapWitnessRunner new];
109
+ runner.inputJson = inputJson;
110
+ runner.resolve = resolve;
111
+ runner.reject = reject;
112
+ runner.chunks = [NSMutableDictionary new];
113
+ [runner start];
114
+ });
115
+ }
116
+
117
+ - (void)start
118
+ {
119
+ NSError *jsonError = nil;
120
+ NSData *inputData = [self.inputJson dataUsingEncoding:NSUTF8StringEncoding];
121
+ NSDictionary *input = [NSJSONSerialization JSONObjectWithData:inputData options:0 error:&jsonError];
122
+ if (![input isKindOfClass:NSDictionary.class]) {
123
+ [self fail:[NSString stringWithFormat:@"invalid WKWebView witness input: %@", jsonError]];
124
+ return;
125
+ }
126
+ NSString *inputError = nil;
127
+ if (!ZkapValidateWitnessInput(input, &inputError)) {
128
+ [self fail:inputError ?: @"invalid WKWebView witness input"];
129
+ return;
130
+ }
131
+ id runId = input[@"run_id"];
132
+ self.runId = [runId isKindOfClass:NSString.class] ? runId : NSUUID.UUID.UUIDString;
133
+
134
+ WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
135
+ configuration.userContentController = [WKUserContentController new];
136
+ [configuration.userContentController addScriptMessageHandler:self name:@"zkapWitness"];
137
+
138
+ self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
139
+ self.webView.navigationDelegate = self;
140
+ self.webView.hidden = YES;
141
+ [ZkapActiveWitnessRunners() addObject:self];
142
+ [self.webView loadHTMLString:[self htmlForInputJson:self.inputJson] baseURL:nil];
143
+
144
+ __weak ZkapWitnessRunner *weakSelf = self;
145
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(120 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
146
+ ZkapWitnessRunner *strongSelf = weakSelf;
147
+ if (strongSelf && !strongSelf.finished) {
148
+ [strongSelf fail:@"WKWebView witness timed out"];
149
+ }
150
+ });
151
+ }
152
+
153
+ - (NSString *)htmlForInputJson:(NSString *)inputJson
154
+ {
155
+ NSData *literalData = [NSJSONSerialization dataWithJSONObject:@[inputJson ?: @""]
156
+ options:0
157
+ error:nil];
158
+ NSString *inputLiteral = [[NSString alloc] initWithData:literalData encoding:NSUTF8StringEncoding];
159
+ NSMutableString *html = [NSMutableString string];
160
+ [html appendString:@"<!doctype html><html><body><script>\n"];
161
+ [html appendString:@"(function(){\n"];
162
+ [html appendString:@"const inputJson = "];
163
+ [html appendString:inputLiteral];
164
+ [html appendString:@"[0];\n"];
165
+ [html appendString:@"const input = JSON.parse(inputJson);\n"];
166
+ [html appendString:@"const runId = input.run_id;\n"];
167
+ [html appendString:@"function post(message){ window.webkit.messageHandlers.zkapWitness.postMessage(message); }\n"];
168
+ [html appendString:@"function fail(error){ const message = error && error.stack ? error.stack : String(error); post({type:'error', run_id:runId, message}); }\n"];
169
+ [html appendString:@"function b64ToBytes(b64){ const binary = atob(b64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); return bytes; }\n"];
170
+ [html appendString:@"function utf8(bytes){ if (typeof TextDecoder !== 'undefined') return new TextDecoder().decode(bytes); let out = ''; for (let i = 0; i < bytes.length; i++) out += String.fromCharCode(bytes[i]); return out; }\n"];
171
+ [html appendString:@"function bytesToBase64(bytes){ let binary = ''; const step = 0x8000; for (let i = 0; i < bytes.length; i += step) { const slice = bytes.subarray(i, i + step); binary += String.fromCharCode.apply(null, slice); } return btoa(binary); }\n"];
172
+ [html appendString:@"function postBase64Chunks(bytes, chunkSize){ const base64ChunkSize = Math.max(4, Number(chunkSize || 4194304)); let rawChunkSize = Math.floor(base64ChunkSize / 4) * 3; rawChunkSize = Math.max(3, rawChunkSize - (rawChunkSize % 3)); const totalChunks = Math.max(1, Math.ceil(bytes.length / rawChunkSize)); let base64Length = 0; for (let index = 0; index < totalChunks; index++) { const start = index * rawChunkSize; const end = Math.min(bytes.length, start + rawChunkSize); const payload = bytesToBase64(bytes.subarray(start, end)); base64Length += payload.length; post({type:'chunk', run_id:runId, chunk_index:index, total_chunks:totalChunks, chunk_bytes:payload.length, payload_chunk:payload}); } return {totalChunks, base64Length}; }\n"];
173
+ [html appendString:@"async function main(){ const totalStart = performance.now(); const wasmBytes = b64ToBytes(input.wasm_base64); const requestBytes = b64ToBytes(input.request_json_base64); const configBytes = b64ToBytes(input.config_json_base64); const instantiated = await WebAssembly.instantiate(wasmBytes, {}); const instance = instantiated.instance || instantiated; const exports = instance.exports; const memory = exports.memory; if (!memory) throw new Error('wasm export `memory` missing'); const reqPtr = exports.wg_alloc(requestBytes.length); const cfgPtr = exports.wg_alloc(configBytes.length); new Uint8Array(memory.buffer, reqPtr, requestBytes.length).set(requestBytes); new Uint8Array(memory.buffer, cfgPtr, configBytes.length).set(configBytes); const nRaw = exports.synthesize_witness(reqPtr, requestBytes.length, cfgPtr, configBytes.length); const n = typeof nRaw === 'bigint' ? Number(nRaw) : Number(nRaw); exports.wg_dealloc(reqPtr, requestBytes.length); exports.wg_dealloc(cfgPtr, configBytes.length); if (n < 0) { const errPtr = exports.wg_last_error_ptr(); const errLen = exports.wg_last_error_len(); const errBytes = new Uint8Array(memory.buffer, errPtr, errLen).slice(); throw new Error('wasm synthesize_witness reported: ' + utf8(errBytes)); } const outPtr = exports.wg_last_output_ptr(); const output = new Uint8Array(memory.buffer, outPtr, n).slice(); const chunks = postBase64Chunks(output, input.chunk_size); post({type:'done', run_id:runId, byte_length:n, base64_length:chunks.base64Length, total_chunks:chunks.totalChunks, timing_ms:performance.now() - totalStart}); }\n"];
174
+ [html appendString:@"main().catch(fail);\n"];
175
+ [html appendString:@"})();\n"];
176
+ [html appendString:@"</script></body></html>\n"];
177
+ return html;
178
+ }
179
+
180
+ - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
181
+ {
182
+ if (self.finished) {
183
+ return;
184
+ }
185
+ NSDictionary *body = [message.body isKindOfClass:NSDictionary.class] ? message.body : nil;
186
+ if (!body) {
187
+ [self fail:@"WKWebView witness sent a non-object message"];
188
+ return;
189
+ }
190
+ NSString *messageRunId = [body[@"run_id"] isKindOfClass:NSString.class] ? body[@"run_id"] : nil;
191
+ if (![messageRunId isEqualToString:self.runId]) {
192
+ [self fail:@"WKWebView witness run_id mismatch"];
193
+ return;
194
+ }
195
+ NSString *type = [body[@"type"] isKindOfClass:NSString.class] ? body[@"type"] : nil;
196
+ if ([type isEqualToString:@"chunk"]) {
197
+ [self handleChunk:body];
198
+ } else if ([type isEqualToString:@"done"]) {
199
+ [self handleDone:body];
200
+ } else if ([type isEqualToString:@"error"]) {
201
+ NSString *errorMessage = [body[@"message"] isKindOfClass:NSString.class] ? body[@"message"] : @"WKWebView witness failed";
202
+ [self fail:errorMessage];
203
+ } else {
204
+ [self fail:@"WKWebView witness sent an unknown message type"];
205
+ }
206
+ }
207
+
208
+ - (void)handleChunk:(NSDictionary *)body
209
+ {
210
+ NSNumber *indexNumber = [body[@"chunk_index"] isKindOfClass:NSNumber.class] ? body[@"chunk_index"] : nil;
211
+ NSNumber *totalNumber = [body[@"total_chunks"] isKindOfClass:NSNumber.class] ? body[@"total_chunks"] : nil;
212
+ NSNumber *chunkBytesNumber = [body[@"chunk_bytes"] isKindOfClass:NSNumber.class] ? body[@"chunk_bytes"] : nil;
213
+ NSString *payload = [body[@"payload_chunk"] isKindOfClass:NSString.class] ? body[@"payload_chunk"] : nil;
214
+ if (!indexNumber || !totalNumber || !chunkBytesNumber || !payload) {
215
+ [self fail:@"WKWebView witness chunk is missing required fields"];
216
+ return;
217
+ }
218
+
219
+ NSInteger index = indexNumber.integerValue;
220
+ NSInteger total = totalNumber.integerValue;
221
+ if (total <= 0 || index < 0 || index >= total) {
222
+ [self fail:@"WKWebView witness chunk index out of range"];
223
+ return;
224
+ }
225
+ if (self.totalChunks == 0) {
226
+ self.totalChunks = total;
227
+ } else if (self.totalChunks != total) {
228
+ [self fail:@"WKWebView witness total_chunks changed"];
229
+ return;
230
+ }
231
+ NSNumber *key = @(index);
232
+ if (self.chunks[key] != nil) {
233
+ [self fail:@"WKWebView witness sent a duplicate chunk"];
234
+ return;
235
+ }
236
+ if (payload.length != chunkBytesNumber.unsignedIntegerValue) {
237
+ [self fail:@"WKWebView witness chunk length mismatch"];
238
+ return;
239
+ }
240
+ self.chunks[key] = payload;
241
+ self.base64Length += payload.length;
242
+ }
243
+
244
+ - (void)handleDone:(NSDictionary *)body
245
+ {
246
+ NSNumber *byteLengthNumber = [body[@"byte_length"] isKindOfClass:NSNumber.class] ? body[@"byte_length"] : nil;
247
+ NSNumber *base64LengthNumber = [body[@"base64_length"] isKindOfClass:NSNumber.class] ? body[@"base64_length"] : nil;
248
+ NSNumber *totalChunksNumber = [body[@"total_chunks"] isKindOfClass:NSNumber.class] ? body[@"total_chunks"] : nil;
249
+ if (!byteLengthNumber || !base64LengthNumber || !totalChunksNumber) {
250
+ [self fail:@"WKWebView witness completion is missing required fields"];
251
+ return;
252
+ }
253
+ NSInteger total = totalChunksNumber.integerValue;
254
+ if (total <= 0 || self.totalChunks != total || self.chunks.count != (NSUInteger)total) {
255
+ [self fail:@"WKWebView witness chunk count mismatch"];
256
+ return;
257
+ }
258
+ NSMutableString *joined = [NSMutableString stringWithCapacity:self.base64Length];
259
+ for (NSInteger i = 0; i < total; i++) {
260
+ NSString *chunk = self.chunks[@(i)];
261
+ if (!chunk) {
262
+ [self fail:@"WKWebView witness missing chunk"];
263
+ return;
264
+ }
265
+ [joined appendString:chunk];
266
+ }
267
+ if (joined.length != base64LengthNumber.unsignedIntegerValue) {
268
+ [self fail:@"WKWebView witness base64 length mismatch"];
269
+ return;
270
+ }
271
+
272
+ NSData *decoded = [[NSData alloc] initWithBase64EncodedString:joined options:0];
273
+ if (!decoded || decoded.length != byteLengthNumber.unsignedIntegerValue) {
274
+ [self fail:@"WKWebView witness decoded length mismatch"];
275
+ return;
276
+ }
277
+ NSString *sha256 = ZkapSha256Hex(decoded);
278
+ NSString *fileName = [NSString stringWithFormat:@"zkap-witness-%@.bin", self.runId];
279
+ NSString *witnessPath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
280
+ NSError *writeError = nil;
281
+ if (![decoded writeToFile:witnessPath options:NSDataWritingAtomic error:&writeError]) {
282
+ [self fail:[NSString stringWithFormat:@"WKWebView witness file write failed: %@", writeError.localizedDescription]];
283
+ return;
284
+ }
285
+ NSDictionary *result = @{
286
+ @"witnessBundlePath": witnessPath,
287
+ @"sha256": sha256,
288
+ @"byteLength": @(decoded.length),
289
+ @"base64Length": @(joined.length),
290
+ @"chunkCount": @(total),
291
+ @"timingMs": body[@"timing_ms"] ?: @0,
292
+ };
293
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:0 error:nil];
294
+ NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
295
+ self.resolve(json);
296
+ [self finish];
297
+ }
298
+
299
+ - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
300
+ {
301
+ [self fail:[NSString stringWithFormat:@"WKWebView witness navigation failed: %@", error.localizedDescription]];
302
+ }
303
+
304
+ - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
305
+ {
306
+ [self fail:[NSString stringWithFormat:@"WKWebView witness provisional navigation failed: %@", error.localizedDescription]];
307
+ }
308
+
309
+ - (void)fail:(NSString *)message
310
+ {
311
+ if (self.finished) {
312
+ return;
313
+ }
314
+ self.reject(@"zkap-witness-webview", message, nil);
315
+ [self finish];
316
+ }
317
+
318
+ - (void)finish
319
+ {
320
+ if (self.finished) {
321
+ return;
322
+ }
323
+ self.finished = YES;
324
+ [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"zkapWitness"];
325
+ self.webView.navigationDelegate = nil;
326
+ [self.webView stopLoading];
327
+ self.webView = nil;
328
+ [ZkapActiveWitnessRunners() removeObject:self];
329
+ }
330
+
331
+ @end
332
+
333
+ namespace uniffi_generated {
334
+ using namespace facebook::react;
335
+ /**
336
+ * ObjC++ class for module 'NativeZkapReactNative'
337
+ */
338
+ class JSI_EXPORT NativeZkapReactNativeSpecJSI : public ObjCTurboModule {
339
+ public:
340
+ NativeZkapReactNativeSpecJSI(const ObjCTurboModule::InitParams &params);
341
+ std::shared_ptr<CallInvoker> callInvoker;
342
+ };
343
+
344
+ static facebook::jsi::Value __hostFunction_ZkapReactNative_installRustCrate(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
345
+ auto& tm = static_cast<NativeZkapReactNativeSpecJSI&>(turboModule);
346
+ auto jsInvoker = tm.callInvoker;
347
+ uint8_t result = baerae_zkapreactnative::installRustCrate(rt, jsInvoker);
348
+ return facebook::jsi::Value(rt, result);
349
+ }
350
+ static facebook::jsi::Value __hostFunction_ZkapReactNative_cleanupRustCrate(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
351
+ uint8_t result = baerae_zkapreactnative::cleanupRustCrate(rt);
352
+ return facebook::jsi::Value(rt, result);
353
+ }
354
+ static facebook::jsi::Value __hostFunction_ZkapReactNative_runWitnessInWkWebView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
355
+ return static_cast<ObjCTurboModule &>(turboModule).invokeObjCMethod(
356
+ rt,
357
+ PromiseKind,
358
+ "runWitnessInWkWebView",
359
+ @selector(runWitnessInWkWebView:resolve:reject:),
360
+ args,
361
+ count);
362
+ }
363
+
364
+ NativeZkapReactNativeSpecJSI::NativeZkapReactNativeSpecJSI(const ObjCTurboModule::InitParams &params)
365
+ : ObjCTurboModule(params), callInvoker(params.jsInvoker) {
366
+ this->methodMap_["installRustCrate"] = MethodMetadata {1, __hostFunction_ZkapReactNative_installRustCrate};
367
+ this->methodMap_["cleanupRustCrate"] = MethodMetadata {1, __hostFunction_ZkapReactNative_cleanupRustCrate};
368
+ this->methodMap_["runWitnessInWkWebView"] = MethodMetadata {1, __hostFunction_ZkapReactNative_runWitnessInWkWebView};
369
+ }
370
+ } // namespace uniffi_generated
371
+
372
+ @implementation ZkapReactNative
373
+ RCT_EXPORT_MODULE()
374
+
375
+ // Don't compile this code when we build for the old architecture.
376
+ #ifdef RCT_NEW_ARCH_ENABLED
377
+
378
+ // Automated testing checks baerae_zkapreactnative
379
+ // by comparing the whole line here.
380
+ /*
381
+ - (NSNumber *)multiply:(double)a b:(double)b {
382
+ NSNumber *result = @(baerae_zkapreactnative::multiply(a, b));
383
+ }
384
+ */
385
+
386
+ - (NSNumber *)installRustCrate {
387
+ @throw [NSException exceptionWithName:@"UnreachableException"
388
+ reason:@"This method should never be called."
389
+ userInfo:nil];
390
+ }
391
+
392
+ - (NSNumber *)cleanupRustCrate {
393
+ @throw [NSException exceptionWithName:@"UnreachableException"
394
+ reason:@"This method should never be called."
395
+ userInfo:nil];
396
+ }
397
+
398
+ - (void)runWitnessInWkWebView:(NSString *)inputJson
399
+ resolve:(RCTPromiseResolveBlock)resolve
400
+ reject:(RCTPromiseRejectBlock)reject
401
+ {
402
+ [ZkapWitnessRunner runWithInputJson:inputJson resolve:resolve reject:reject];
403
+ }
404
+
405
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
406
+ (const facebook::react::ObjCTurboModule::InitParams &)params
407
+ {
408
+ return std::make_shared<uniffi_generated::NativeZkapReactNativeSpecJSI>(params);
409
+ }
410
+ #endif
411
+
412
+ @end
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@baerae/zkap-zkp-react-native",
3
- "version": "0.1.4",
4
- "description": "React Native SDK for zkap-zkp — on-device ZK proving with Groth16 (BN254) and Poseidon hash. Requires Expo New Architecture.",
3
+ "version": "0.1.5",
4
+ "description": "React Native SDK for zkap-zkp — on-device ZK proving with Groth16 (BN254) and Poseidon hash.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "source": "./src/index.ts",
@@ -9,16 +9,24 @@
9
9
  "files": [
10
10
  "README.md",
11
11
  "src/",
12
- "ios/ZkapSdkModule.podspec",
13
- "ios/ZkapSdkModule.swift",
14
- "ios/uniffi/",
12
+ "cpp/",
13
+ "ios/ZkapReactNative.h",
14
+ "ios/ZkapReactNative.mm",
15
15
  "ios/ZkapZkp.xcframework/",
16
- "android/src/",
16
+ "ZkapReactNative.podspec",
17
+ "android/src/main/AndroidManifest.xml",
18
+ "android/src/main/java/com/baerae/zkapreactnative/",
19
+ "android/CMakeLists.txt",
20
+ "android/cpp-adapter.cpp",
17
21
  "android/build.gradle",
18
- "android/libs/",
19
- "expo-module.config.json"
22
+ "android/libs/arm64-v8a/libzkap_uniffi_bindings.so",
23
+ "android/libs/x86_64/libzkap_uniffi_bindings.so",
24
+ "android/proguard-rules.pro",
25
+ "ubrn.config.yaml"
20
26
  ],
21
27
  "scripts": {
28
+ "generate:ubrn": "uniffi-bindgen-react-native generate jsi bindings --library --crate zkap_uniffi_bindings --ts-dir src/generated --cpp-dir cpp/generated ../../target/debug/libzkap_uniffi_bindings.dylib",
29
+ "prepack": "node ../../scripts/check-rn-native-artifacts.mjs",
22
30
  "typecheck": "tsc --noEmit"
23
31
  },
24
32
  "engines": {
@@ -30,23 +38,38 @@
30
38
  "poseidon",
31
39
  "zkap",
32
40
  "react-native",
33
- "expo",
41
+ "ubrn",
34
42
  "zero-knowledge"
35
43
  ],
36
44
  "repository": {
37
45
  "type": "git",
38
- "url": "git+https://github.com/baerae-zkap/zkap-zkp.git",
46
+ "url": "git+https://github.com/baerae-zkap/zkap-zkp-sdk.git",
39
47
  "directory": "packages/sdk-react-native"
40
48
  },
41
- "homepage": "https://github.com/baerae-zkap/zkap-zkp",
42
- "bugs": "https://github.com/baerae-zkap/zkap-zkp/issues",
49
+ "homepage": "https://github.com/baerae-zkap/zkap-zkp-sdk",
50
+ "bugs": "https://github.com/baerae-zkap/zkap-zkp-sdk/issues",
43
51
  "author": "baerae-zkap",
44
52
  "license": "MIT OR Apache-2.0",
45
53
  "peerDependencies": {
46
- "expo": ">=50.0.0",
47
- "expo-modules-core": ">=1.12.0",
54
+ "react": ">=18",
48
55
  "react-native": ">=0.73.0"
49
56
  },
57
+ "peerDependenciesMeta": {
58
+ "react": {
59
+ "optional": true
60
+ },
61
+ "react-native": {
62
+ "optional": true
63
+ }
64
+ },
65
+ "dependencies": {
66
+ "uniffi-bindgen-react-native": "^0.31.0-2"
67
+ },
68
+ "codegenConfig": {
69
+ "name": "ZkapReactNativeSpec",
70
+ "type": "modules",
71
+ "jsSrcsDir": "src"
72
+ },
50
73
  "devDependencies": {
51
74
  "typescript": "^5.4.5"
52
75
  },
@@ -0,0 +1,11 @@
1
+ // Generated by uniffi-bindgen-react-native
2
+ import type { TurboModule } from 'react-native';
3
+ import { TurboModuleRegistry } from 'react-native';
4
+
5
+ export interface Spec extends TurboModule {
6
+ installRustCrate(): boolean;
7
+ cleanupRustCrate(): boolean;
8
+ runWitnessInWkWebView(inputJson: string): Promise<string>;
9
+ }
10
+
11
+ export default TurboModuleRegistry.getEnforcing<Spec>('ZkapReactNative');