zano-native 0.0.1

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 (48) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/LICENSE +27 -0
  3. package/README.md +24 -0
  4. package/android/build.gradle +40 -0
  5. package/android/src/main/java/app/edge/rnzano/RnZanoModule.java +73 -0
  6. package/android/src/main/java/app/edge/rnzano/RnZanoPackage.java +21 -0
  7. package/android/src/main/jniLibs/arm64-v8a/librnzano.so +0 -0
  8. package/ios/ZanoModule.h +4 -0
  9. package/ios/ZanoModule.mm +138 -0
  10. package/ios/ZanoModule.xcframework/Info.plist +43 -0
  11. package/ios/ZanoModule.xcframework/ios-arm64/libzano-module.a +0 -0
  12. package/ios/ZanoModule.xcframework/ios-arm64-simulator/libzano-module.a +0 -0
  13. package/ios/react-native-zano.xcodeproj/project.pbxproj +1 -0
  14. package/lib/scripts/build-native-host.d.ts +1 -0
  15. package/lib/scripts/build-native-host.js +175 -0
  16. package/lib/scripts/smoke-node.d.ts +1 -0
  17. package/lib/scripts/smoke-node.js +33 -0
  18. package/lib/scripts/update-sources.d.ts +1 -0
  19. package/lib/scripts/update-sources.js +412 -0
  20. package/lib/scripts/utils/android-tools.d.ts +1 -0
  21. package/lib/scripts/utils/android-tools.js +25 -0
  22. package/lib/scripts/utils/closeWalletPatch.d.ts +23 -0
  23. package/lib/scripts/utils/closeWalletPatch.js +215 -0
  24. package/lib/scripts/utils/common.d.ts +37 -0
  25. package/lib/scripts/utils/common.js +186 -0
  26. package/lib/scripts/utils/ios-tools.d.ts +8 -0
  27. package/lib/scripts/utils/ios-tools.js +26 -0
  28. package/lib/scripts/utils/sdkFolders.d.ts +27 -0
  29. package/lib/scripts/utils/sdkFolders.js +43 -0
  30. package/lib/src/CppBridge.d.ts +142 -0
  31. package/lib/src/CppBridge.js +668 -0
  32. package/lib/src/index.d.ts +4 -0
  33. package/lib/src/index.js +28 -0
  34. package/lib/src/load-addon.d.ts +5 -0
  35. package/lib/src/load-addon.js +50 -0
  36. package/lib/src/node.d.ts +11 -0
  37. package/lib/src/node.js +25 -0
  38. package/lib/src/types.d.ts +292 -0
  39. package/lib/src/types.js +40 -0
  40. package/lib/src/walletFilePassword.d.ts +14 -0
  41. package/lib/src/walletFilePassword.js +74 -0
  42. package/node.d.ts +5 -0
  43. package/node.js +2 -0
  44. package/package.json +105 -0
  45. package/prebuilds/darwin-arm64/zano.node +0 -0
  46. package/src/node/zano-napi.cpp +176 -0
  47. package/src/zano-wrapper/zano-methods.hpp +15 -0
  48. package/zano-native.podspec +27 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,90 @@
1
+ # zano-native
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.5.1 (2026-08-28)
6
+
7
+ - changed: `getTransactions` queries `get_recent_txs_and_info3`, the HF6-ready history endpoint. The old endpoint reported only the deprecated transaction-wide payment id - an empty string for every id created since HF6, when ids moved into individual outputs - and its legacy serializer refuses entries that carry more than one distinct per-output id, which the HF6 migration guide says to expect. `RecentTransaction` now exposes `subtransfers_by_pid`, amounts grouped by hex payment id, in place of the flat `subtransfers` list and the always-stale `payment_id` field, along with the sender-recorded `remote_addresses`.
8
+ - fixed: Android runs native Zano calls on a dedicated thread instead of React Native's shared native-modules thread. On the legacy architecture that shared thread also executes UIManager's view commands, so a Zano call that blocked in C++ froze every view update in the app - taps dead, screens frozen - while native scrolling kept working. A blocked call now stalls only Zano.
9
+ - fixed: Closing a wallet while it is catching up on blocks no longer deadlocks the native wallet manager permanently on Android. The SDK's `close_wallet` held the wallet-manager lock while waiting out the wallet's refresh worker, which needed that same lock to reach its stop flags, so the close, the worker, and every Zano call after them hung forever; the app's periodic mid-sync saves tripped this within minutes of importing a wallet. The Android build now rewrites `close_wallet` to detach the wallet from the manager before waiting on it. iOS links the prebuilt Zano framework and runs the same cycle without wedging, so it is unchanged.
10
+ - fixed: A Zano wallet file the SDK cannot read - typically zero bytes, left by a crash during the file's very first write - no longer traps the wallet in a permanent open-retry loop. Such a file fails with `INVALID_FILE` before any password is consulted, which bypassed the password-recovery ladder entirely, so the engine retried the same doomed open every second forever. The file is now deleted and rebuilt from the mnemonic, costing one re-scan, under the same policy as a file no password opens: a wallet with a seed passphrase still refuses, since an unreadable file cannot corroborate the passphrase and a wrong one would rebuild a different wallet.
11
+
12
+ ## 0.5.0 (2026-08-25)
13
+
14
+ - added: `runWallet`, which starts the refresh worker for an open wallet. `startWallet` rethrows `ALREADY_EXISTS` for its caller to adopt the already-open wallet, and an adopted wallet does not sync until it is run, so adopting callers need this without reimplementing the raw `run_wallet` response contract.
15
+ - changed: `transfer` no longer takes a `paymentId`, and never sends the request-level payment id. Zano HF6 deprecated the transaction-wide payment id -- the node rejects any non-empty value -- and instead delivers payment ids per destination, embedded in integrated addresses, which the wallet attaches natively. Forwarding the option therefore failed every send that carried one. A caller holding a separate payment id must fold it into an integrated destination address before calling. The per-destination address-info loop is also gone: it read fields the native `get_address_info` has never returned, so it never did anything except spend a native round-trip per recipient.
16
+ - changed: The `AddressInfo` type now matches the native response: `payment_id` is a boolean presence flag, and `is_integrated` does not exist. Both were previously declared with shapes no native version ever produced.
17
+ - fixed: `startWallet` no longer freezes the app while a wallet catches up on blocks. Opening a wallet auto-started its refresh worker, which holds the per-wallet lock for the entire first scan, so the 0.4.0 re-key migration's `resetWalletPassword` blocked on that lock for the whole catch-up (minutes to hours) while sitting on React Native's shared native-module queue, and on iOS every native call in the app queued behind it. Wallets now open with the refresh worker postponed, the migration completes in milliseconds, and the worker is started explicitly for the one wallet `startWallet` returns.
18
+ - fixed: Creating a wallet no longer freezes the app. `generateSeedPhrase` opened its temporary wallet without postponing the refresh worker, so the `closeWallet` that follows waited on the per-wallet lock, on the shared native-module queue, for the length of a refresh. A close that does not report OK now fails the call rather than deleting a file this process still holds open.
19
+ - fixed: The iOS module now withholds its document directory when the wallet directory cannot be created or excluded from device backups, so the bridge fails at construction instead of letting the SDK write seed and spend keys somewhere a backup would capture.
20
+
21
+ ## 0.4.0 (2026-08-14)
22
+
23
+ - added: `startWallet` accepts an optional `log` callback that reports wallet-file recovery and migration events.
24
+ - added: A `ZanoError` class carrying the native API return code as `error.code`. Thrown error messages keep their existing `<code> <message>` shape.
25
+ - added: A unit-test suite (`npm test`) that runs the bridge against a fake native module.
26
+ - added: `update-sources.ts` fails the build if the Zano SDK declares a directory this package does not create and exclude from backups, so bumping the SDK pin cannot silently introduce an unprotected one.
27
+ - changed: Update `zano_native_lib` to `91085c0` for Zano HF6 support.
28
+ - changed: Build the iOS library against the prebuilt `libzano-plain-wallet` xcframework, since `zano_native_lib` no longer ships the raw `_libs_ios` OpenSSL and Boost archives.
29
+ - changed: Fetch only the pinned commit and the Git LFS objects we build against, instead of cloning the full history along with every platform's prebuilt archives.
30
+ - fixed: Native failures reported as success-shaped payloads (`INTERNAL_ERROR`, `UNINITIALIZED`) now throw instead of resolving with a wallet whose `wallet_id` is undefined.
31
+ - fixed: `generateSeedPhrase` no longer leaves a temporary wallet file on disk, encrypted with the seed passphrase, as a side effect of generating a seed.
32
+ - security: `startWallet` now encrypts the wallet file with a password derived from the mnemonic, instead of with the seed passphrase, which is the empty string for most wallets, leaving the seed and spend keys effectively unencrypted on disk. Files written by earlier versions are re-keyed in place the first time they open. A file that no known password opens is deleted and rebuilt from the mnemonic, costing one re-scan, unless the wallet has a seed passphrase. That case throws instead, since a passphrase that does not match the file would otherwise rebuild a different wallet over an intact one.
33
+ - security: Exclude every iOS directory the SDK writes into -- `wallets`, `logs` and `app_config` -- from device backups, so the wallet files no longer reach an unencrypted Finder backup. Android stores these in private app storage and already sets `android:allowBackup="false"`.
34
+
35
+ ## 0.3.0 (2026-06-13)
36
+
37
+ - changed: Convert the build tooling from Yarn to npm.
38
+ - security: Upgrade dependencies per Socket security recommendations.
39
+
40
+ ## 0.2.8 (2026-02-23)
41
+
42
+ - fixed: Remove invalid const qualifier from std::vector element types to fix Xcode 26 builds
43
+
44
+ ## 0.2.7 (2026-02-15)
45
+
46
+ - fixed: Add missing `wrap` and `auditable` fields to `AddressInfo` type
47
+
48
+ ## 0.2.6 (2026-01-29)
49
+
50
+ - changed: Update `zano_native_lib` to `239d4a39`
51
+
52
+ ## 0.2.5 (2025-11-17)
53
+
54
+ - fixed: Show proper messages for C++ errors.
55
+
56
+ ## 0.2.4 (2025-09-19)
57
+
58
+ - fixed: Re-publish with correctly-built binaries.
59
+
60
+ ## 0.2.3 (2025-09-18)
61
+
62
+ - fixed: Fix `burnAsset` return value and types
63
+
64
+ ## 0.2.2 (2025-09-09)
65
+
66
+ - added: Add `burnAsset` method
67
+
68
+ ## 0.2.1 (2025-07-21)
69
+
70
+ - fixed: Support 16k pages for Android 15.
71
+
72
+ ## 0.2.0 (2025-05-26)
73
+
74
+ - changed: `transfer` now accepts an array of destinations.
75
+
76
+ ## 0.1.3 (2025-05-05)
77
+
78
+ - fixed: Shield symbols for iOS builds using partial linking and symbol encapsulation.
79
+
80
+ ## 0.1.2 (2025-04-29)
81
+
82
+ - fixed: Republish with missing .so files
83
+
84
+ ## 0.1.1 (2025-04-25)
85
+
86
+ - fixed: package.json types path
87
+
88
+ ## 0.1.0 (2025-04-25)
89
+
90
+ - Initial release
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Airbitz, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This package wraps Zano, which is MIT, and ships binaries compiled from it.
26
+ Boost, OpenSSL and the other C++ dependencies the build fetches are also
27
+ compiled in and remain under their own licenses.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # zano-native
2
+
3
+ Native Zano wallet library for React Native and Node.js.
4
+
5
+ The compiled SDK lives in this package. React Native and Node are hosts.
6
+
7
+ - GitHub: [EdgeApp/zano-native](https://github.com/EdgeApp/zano-native)
8
+ - npm: `zano-native`
9
+
10
+ ## React Native
11
+
12
+ ```js
13
+ import { makeZano } from 'zano-native'
14
+ ```
15
+
16
+ Mobile binaries: `npm run update-sources`. Not run on `npm install`.
17
+
18
+ ## Node (CLI)
19
+
20
+ ```js
21
+ import { makeNodeZanoModule } from 'zano-native/node'
22
+ ```
23
+
24
+ Host addon: `npm run build-native-host` (explicit; `prepare` / `prepack` compile JavaScript only).
@@ -0,0 +1,40 @@
1
+ buildscript {
2
+ repositories {
3
+ mavenCentral()
4
+ google()
5
+ }
6
+
7
+ dependencies {
8
+ classpath 'com.android.tools.build:gradle:7.3.1'
9
+ }
10
+ }
11
+
12
+ apply plugin: 'com.android.library'
13
+
14
+ def safeExtGet(prop, fallback) {
15
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
16
+ }
17
+
18
+ android {
19
+ compileSdkVersion safeExtGet('compileSdkVersion', 32)
20
+ defaultConfig {
21
+ minSdkVersion safeExtGet('minSdkVersion', 23)
22
+ targetSdkVersion safeExtGet('targetSdkVersion', 32)
23
+ }
24
+ lintOptions {
25
+ abortOnError false
26
+ }
27
+ namespace 'app.edge.rnzano'
28
+ }
29
+
30
+ repositories {
31
+ maven {
32
+ url('../node_modules/react-native/android')
33
+ }
34
+ google()
35
+ mavenCentral()
36
+ }
37
+
38
+ dependencies {
39
+ implementation 'com.facebook.react:react-native:+'
40
+ }
@@ -0,0 +1,73 @@
1
+ package app.edge.rnzano;
2
+
3
+ import com.facebook.react.bridge.ReadableArray;
4
+ import com.facebook.react.bridge.Promise;
5
+ import com.facebook.react.bridge.ReactApplicationContext;
6
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
7
+ import com.facebook.react.bridge.ReactMethod;
8
+ import java.util.HashMap;
9
+ import java.util.Map;
10
+ import java.util.concurrent.ExecutorService;
11
+ import java.util.concurrent.Executors;
12
+
13
+ public class RnZanoModule extends ReactContextBaseJavaModule {
14
+ // Zano calls run on their own thread, never on the caller's. On the
15
+ // legacy architecture every @ReactMethod runs on the shared
16
+ // mqt_native_modules thread, which also executes UIManager's view
17
+ // commands - so a Zano call that blocks in C++ (a close waiting out a
18
+ // scan chunk, or the SDK's close-during-scan lock inversion) froze every
19
+ // view update in the app. Static, because the Zano SDK is one instance
20
+ // per process while React module instances are not: a JS reload builds a
21
+ // new module while a call from the old one may still be draining, and
22
+ // per-instance threads would let both drive the SDK at once. One
23
+ // process-wide thread keeps the strict global call ordering, across
24
+ // reloads too. Named, because this freeze was diagnosed from thread
25
+ // dumps, and "pool-1-thread-1" will not identify itself in the next one.
26
+ private static final ExecutorService executor =
27
+ Executors.newSingleThreadExecutor(r -> new Thread(r, "zano"));
28
+
29
+ private native String callZanoJNI(String method, String[] arguments);
30
+
31
+ private native String[] getMethodNames();
32
+
33
+ static {
34
+ System.loadLibrary("rnzano");
35
+ }
36
+
37
+ public RnZanoModule(ReactApplicationContext reactContext) {
38
+ super(reactContext);
39
+ }
40
+
41
+ @Override
42
+ public Map<String, Object> getConstants() {
43
+ final Map<String, Object> constants = new HashMap<>();
44
+ constants.put("methodNames", getMethodNames());
45
+ constants.put("documentDirectory", getReactApplicationContext().getFilesDir().getAbsolutePath());
46
+ return constants;
47
+ }
48
+
49
+ @Override
50
+ public String getName() {
51
+ return "ZanoModule";
52
+ }
53
+
54
+ @ReactMethod
55
+ public void callZano(String method, ReadableArray arguments, Promise promise) {
56
+ // Re-package the arguments:
57
+ String[] strings = new String[arguments.size()];
58
+ for (int i = 0; i < arguments.size(); ++i) {
59
+ strings[i] = arguments.getString(i);
60
+ }
61
+
62
+ executor.execute(() -> {
63
+ try {
64
+ promise.resolve(callZanoJNI(method, strings));
65
+ } catch (Throwable e) {
66
+ // Throwable, not Exception: off the bridge thread an escaping Error
67
+ // reaches the default uncaught handler and kills the process with
68
+ // the promise unsettled; RN's own dispatcher used to catch it.
69
+ promise.reject("ZanoError", e);
70
+ }
71
+ });
72
+ }
73
+ }
@@ -0,0 +1,21 @@
1
+ package app.edge.rnzano;
2
+
3
+ import com.facebook.react.ReactPackage;
4
+ import com.facebook.react.bridge.NativeModule;
5
+ import com.facebook.react.bridge.ReactApplicationContext;
6
+ import com.facebook.react.uimanager.ViewManager;
7
+ import java.util.Arrays;
8
+ import java.util.Collections;
9
+ import java.util.List;
10
+
11
+ public class RnZanoPackage implements ReactPackage {
12
+ @Override
13
+ public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
14
+ return Arrays.<NativeModule>asList(new RnZanoModule(reactContext));
15
+ }
16
+
17
+ @Override
18
+ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
19
+ return Collections.emptyList();
20
+ }
21
+ }
@@ -0,0 +1,4 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface ZanoModule : NSObject <RCTBridgeModule>
4
+ @end
@@ -0,0 +1,138 @@
1
+ #import "ZanoModule.h"
2
+ #import <React/RCTLog.h>
3
+ #include "zano-methods.hpp"
4
+
5
+ @implementation ZanoModule
6
+
7
+ RCT_EXPORT_MODULE();
8
+
9
+ + (BOOL)requiresMainQueueSetup { return NO; }
10
+
11
+ RCT_REMAP_METHOD(
12
+ callZano,
13
+ callZanoMethod:(NSString *)method
14
+ arguments:(NSArray *)arguments
15
+ resolver:(RCTPromiseResolveBlock)resolve
16
+ rejecter:(RCTPromiseRejectBlock)reject
17
+ ) {
18
+ const std::string methodString = [method UTF8String];
19
+
20
+ // Re-package the arguments:
21
+ NSUInteger length = [arguments count];
22
+ std::vector<std::string> strings;
23
+ strings.reserve(length);
24
+ for (NSUInteger i = 0; i < length; ++i) {
25
+ NSString *string = [arguments objectAtIndex:i];
26
+ strings.push_back([string UTF8String]);
27
+ }
28
+
29
+ // Find the named method:
30
+ for (int i = 0; i < zanoMethodCount; ++i) {
31
+ if (zanoMethods[i].name != methodString) continue;
32
+
33
+ // Validate the argument count:
34
+ if (zanoMethods[i].argc != strings.size()) {
35
+ reject(@"Error", @"zano incorrect C++ argument count", nil);
36
+ return;
37
+ }
38
+
39
+ // Call the method, with error handling:
40
+ try {
41
+ const std::string out = zanoMethods[i].method(strings);
42
+ resolve(
43
+ [NSString stringWithCString:out.c_str() encoding:NSUTF8StringEncoding]
44
+ );
45
+ } catch (std::exception &e) {
46
+ reject(
47
+ @"Error",
48
+ [NSString stringWithCString:e.what() encoding:NSUTF8StringEncoding],
49
+ nil
50
+ );
51
+ } catch (...) {
52
+ reject(@"Error", @"zano threw a C++ exception", nil);
53
+ }
54
+ return;
55
+ }
56
+
57
+ reject(
58
+ @"TypeError",
59
+ [NSString stringWithFormat:@"No zano C++ method %@", method],
60
+ nil
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Creates a directory the Zano SDK writes into, and keeps it out of
66
+ * device backups. The wallet files hold the seed and spend keys, so they
67
+ * must not reach an unencrypted Finder backup.
68
+ *
69
+ * The SDK creates these itself on first use, but only we can set the
70
+ * backup flag, and that requires the directory to already exist.
71
+ */
72
+ static BOOL prepareZanoDirectory(NSURL *parent, NSString *name)
73
+ {
74
+ NSURL *url = [parent URLByAppendingPathComponent:name isDirectory:YES];
75
+
76
+ NSError *error = nil;
77
+ if (![[NSFileManager defaultManager] createDirectoryAtURL:url
78
+ withIntermediateDirectories:YES
79
+ attributes:nil
80
+ error:&error]) {
81
+ RCTLogWarn(@"zano could not create %@: %@", name, error);
82
+ return NO;
83
+ }
84
+
85
+ if (![url setResourceValue:@YES
86
+ forKey:NSURLIsExcludedFromBackupKey
87
+ error:&error]) {
88
+ RCTLogWarn(@"zano could not exclude %@ from backups: %@", name, error);
89
+ return NO;
90
+ }
91
+
92
+ return YES;
93
+ }
94
+
95
+ - (NSDictionary *)constantsToExport
96
+ {
97
+ NSMutableArray *out = [NSMutableArray arrayWithCapacity:zanoMethodCount];
98
+ for (int i = 0; i < zanoMethodCount; ++i) {
99
+ NSString *name = [NSString stringWithCString:zanoMethods[i].name
100
+ encoding:NSUTF8StringEncoding];
101
+ out[i] = name;
102
+ }
103
+
104
+ NSFileManager *fileManager = [NSFileManager defaultManager];
105
+ NSError *docsError = nil;
106
+ NSURL *docsDir = [fileManager URLForDirectory:NSDocumentDirectory
107
+ inDomain:NSUserDomainMask
108
+ appropriateForURL:nil
109
+ create:YES
110
+ error:&docsError];
111
+ if (docsDir == nil) {
112
+ RCTLogWarn(@"zano could not resolve the documents directory: %@", docsError);
113
+ return @{ @"methodNames": out };
114
+ }
115
+
116
+ // Every directory the SDK derives from the working directory we hand it.
117
+ // `scripts/update-sources.ts` fails the build if this list drifts from the
118
+ // folder names declared in the SDK's `plain_wallet_api.cpp`.
119
+ //
120
+ // `wallets` holds the seed and spend keys, so failing to create it or to
121
+ // mark it excluded from backups is fatal: withholding
122
+ // `documentDirectory` stops the bridge in its constructor instead of
123
+ // letting the SDK write keys somewhere an unencrypted Finder backup would
124
+ // pick up. `logs` and `app_config` carry no key material.
125
+ BOOL walletsReady = prepareZanoDirectory(docsDir, @"wallets");
126
+ prepareZanoDirectory(docsDir, @"logs");
127
+ prepareZanoDirectory(docsDir, @"app_config");
128
+ if (!walletsReady) {
129
+ return @{ @"methodNames": out };
130
+ }
131
+
132
+ return @{
133
+ @"methodNames": out,
134
+ @"documentDirectory": [docsDir path]
135
+ };
136
+ }
137
+
138
+ @end
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>AvailableLibraries</key>
6
+ <array>
7
+ <dict>
8
+ <key>BinaryPath</key>
9
+ <string>libzano-module.a</string>
10
+ <key>LibraryIdentifier</key>
11
+ <string>ios-arm64</string>
12
+ <key>LibraryPath</key>
13
+ <string>libzano-module.a</string>
14
+ <key>SupportedArchitectures</key>
15
+ <array>
16
+ <string>arm64</string>
17
+ </array>
18
+ <key>SupportedPlatform</key>
19
+ <string>ios</string>
20
+ </dict>
21
+ <dict>
22
+ <key>BinaryPath</key>
23
+ <string>libzano-module.a</string>
24
+ <key>LibraryIdentifier</key>
25
+ <string>ios-arm64-simulator</string>
26
+ <key>LibraryPath</key>
27
+ <string>libzano-module.a</string>
28
+ <key>SupportedArchitectures</key>
29
+ <array>
30
+ <string>arm64</string>
31
+ </array>
32
+ <key>SupportedPlatform</key>
33
+ <string>ios</string>
34
+ <key>SupportedPlatformVariant</key>
35
+ <string>simulator</string>
36
+ </dict>
37
+ </array>
38
+ <key>CFBundlePackageType</key>
39
+ <string>XFWK</string>
40
+ <key>XCFrameworkFormatVersion</key>
41
+ <string>1.0</string>
42
+ </dict>
43
+ </plist>
@@ -0,0 +1 @@
1
+ // Placeholder so react-native autolinking can find us.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ // Run this script as `node -r sucrase/register ./scripts/build-native-host.ts`
3
+ //
4
+ // Builds a Node N-API addon for the current host:
5
+ // - Fetches zano_native_lib (macOS plain-wallet xcframework via Git LFS)
6
+ // - Compiles zano-methods.cpp and relocatable-links against the prebuilt
7
+ // - Archives to zano-module.a, then node-gyp produces
8
+ // prebuilds/<platform>-<arch>/zano.node
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const promises_1 = require("fs/promises");
11
+ const path_1 = require("path");
12
+ const common_1 = require("./utils/common");
13
+ const ios_tools_1 = require("./utils/ios-tools");
14
+ const srcPath = (0, path_1.join)(__dirname, '../src');
15
+ const zanoNativeHash = '91085c0ebd95fcdae3327071a9e5f5b615d7da3d';
16
+ /**
17
+ * Locates the slice of a prebuilt macOS xcframework that matches this arch.
18
+ */
19
+ async function findMacosXcframeworkSlice(frameworkPath, arch) {
20
+ const plist = JSON.parse(await (0, common_1.captureExec)('plutil', [
21
+ '-convert',
22
+ 'json',
23
+ '-o',
24
+ '-',
25
+ (0, path_1.join)(frameworkPath, 'Info.plist')
26
+ ]));
27
+ const slice = plist.AvailableLibraries.find(library => library.SupportedPlatform === 'macos' &&
28
+ library.SupportedArchitectures.includes(arch));
29
+ if (slice == null) {
30
+ throw new Error(`${frameworkPath} has no macos slice for ${arch}. It offers: ${plist.AvailableLibraries.map(library => library.LibraryIdentifier).join(', ')}`);
31
+ }
32
+ const slicePath = (0, path_1.join)(frameworkPath, slice.LibraryIdentifier);
33
+ const libraryPath = (0, path_1.join)(slicePath, slice.LibraryPath);
34
+ const archs = (await (0, common_1.captureExec)('lipo', ['-archs', libraryPath])).split(/\s+/);
35
+ if (!archs.includes(arch)) {
36
+ throw new Error(`${libraryPath} holds ${archs.join(', ')}, but we are building ${arch}`);
37
+ }
38
+ return {
39
+ headersPath: (0, path_1.join)(slicePath, slice.HeadersPath ?? 'Headers'),
40
+ libraryPath
41
+ };
42
+ }
43
+ async function downloadSources() {
44
+ await (0, common_1.getRepo)('zano_native_lib', 'https://github.com/hyle-team/zano_native_lib.git', zanoNativeHash,
45
+ // Host builds only need the macOS plain-wallet prebuild (+ headers inside):
46
+ { lfsIncludes: ['_install_macosx/**'] });
47
+ // Rename the compress function in RIPEMD160.c,
48
+ // since that conflicts with Zlib (same patch as update-sources.ts):
49
+ const mdPath = (0, path_1.join)(common_1.tmpPath, 'zano_native_lib/Zano/src/crypto/RIPEMD160.h');
50
+ if (await (0, common_1.fileExists)(mdPath)) {
51
+ const mdText = await (0, promises_1.readFile)(mdPath, 'utf8');
52
+ if (!mdText.includes('#define compress md_compress')) {
53
+ await (0, promises_1.writeFile)(mdPath, '#define compress md_compress\n' + mdText);
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Compiles the wrapper and links it against the prebuilt macOS
59
+ * libzano-plain-wallet static library into zano-module.a.
60
+ */
61
+ async function buildHostStaticLib(arch) {
62
+ const working = (0, path_1.join)(common_1.tmpPath, `host-darwin-${arch}`);
63
+ await (0, promises_1.mkdir)(working, { recursive: true });
64
+ const { headersPath, libraryPath: zanoLib } = await findMacosXcframeworkSlice((0, path_1.join)(common_1.tmpPath, 'zano_native_lib/_install_macosx/libzano-plain-wallet.xcframework'), arch);
65
+ const ar = await (0, common_1.quietExec)('xcrun', ['--find', 'ar']);
66
+ const cxx = await (0, common_1.quietExec)('xcrun', ['--find', 'clang++']);
67
+ const ld = await (0, common_1.quietExec)('xcrun', ['--find', 'ld']);
68
+ const objcopy = await (0, ios_tools_1.getObjcopyPath)();
69
+ const sysroot = await (0, common_1.quietExec)('xcrun', ['--show-sdk-path']);
70
+ const sdkFlags = [
71
+ '-arch',
72
+ arch,
73
+ '-isysroot',
74
+ sysroot,
75
+ '-mmacosx-version-min=11.0'
76
+ ];
77
+ const cxxflags = [
78
+ `-I${headersPath}`,
79
+ '-O2',
80
+ '-std=c++17',
81
+ '-fPIC',
82
+ '-fexceptions'
83
+ ];
84
+ const source = 'zano-wrapper/zano-methods.cpp';
85
+ const object = (0, path_1.join)(working, 'zano-methods.o');
86
+ console.log(`Compiling ${source} for host-darwin-${arch}...`);
87
+ await (0, common_1.loudExec)(cxx, [
88
+ '-c',
89
+ ...cxxflags,
90
+ ...sdkFlags,
91
+ `-o${object}`,
92
+ (0, path_1.join)(srcPath, source)
93
+ ]);
94
+ // Relocatable link against the prebuilt plain-wallet archive
95
+ // (same approach as iOS in update-sources.ts):
96
+ console.log(`Linking zano-module.o for host-darwin-${arch}`);
97
+ const objectPath = (0, path_1.join)(working, 'zano-module.o');
98
+ await (0, common_1.loudExec)(ld, [
99
+ '-r',
100
+ '-arch',
101
+ arch,
102
+ '-o',
103
+ objectPath,
104
+ object,
105
+ zanoLib
106
+ ]);
107
+ // Localize all symbols except the ones we export to the N-API addon:
108
+ await (0, common_1.loudExec)(objcopy, [
109
+ objectPath,
110
+ '-w',
111
+ '-L*',
112
+ '-L!_zanoMethods',
113
+ '-L!_zanoMethodCount'
114
+ ]);
115
+ const library = (0, path_1.join)(working, 'libzano-module.a');
116
+ await (0, promises_1.rm)(library, { force: true });
117
+ await (0, common_1.loudExec)(ar, ['rcs', library, objectPath]);
118
+ console.log(`Built ${library}`);
119
+ return library;
120
+ }
121
+ async function buildNodeAddon(staticLib) {
122
+ const prebuildDir = (0, path_1.join)(__dirname, '../prebuilds', `${process.platform}-${process.arch}`);
123
+ await (0, promises_1.mkdir)(prebuildDir, { recursive: true });
124
+ const napiInclude = String(
125
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
126
+ require((0, path_1.join)(__dirname, '../node_modules/node-addon-api')).include).replace(/"/g, '');
127
+ const gypDir = (0, path_1.join)(common_1.tmpPath, 'nodeaddon');
128
+ await (0, promises_1.mkdir)(gypDir, { recursive: true });
129
+ // node-gyp/make cannot compile sources given as absolute paths.
130
+ await (0, promises_1.copyFile)((0, path_1.join)(__dirname, '../src/node/zano-napi.cpp'), (0, path_1.join)(gypDir, 'zano-napi.cpp'));
131
+ const gyp = {
132
+ targets: [
133
+ {
134
+ target_name: 'zano',
135
+ sources: ['zano-napi.cpp'],
136
+ include_dirs: [napiInclude, (0, path_1.join)(__dirname, '../src/zano-wrapper')],
137
+ defines: ['NAPI_CPP_EXCEPTIONS'],
138
+ 'cflags!': ['-fno-exceptions'],
139
+ 'cflags_cc!': ['-fno-exceptions'],
140
+ cflags_cc: ['-std=c++17', '-fPIC'],
141
+ libraries: [staticLib],
142
+ xcode_settings: {
143
+ GCC_ENABLE_CPP_EXCEPTIONS: 'YES',
144
+ CLANG_CXX_LANGUAGE_STANDARD: 'c++17',
145
+ MACOSX_DEPLOYMENT_TARGET: '11.0',
146
+ OTHER_LDFLAGS: ['-lc++', '-lz']
147
+ }
148
+ }
149
+ ]
150
+ };
151
+ await (0, promises_1.writeFile)((0, path_1.join)(gypDir, 'binding.gyp'), JSON.stringify(gyp, null, 2));
152
+ const nodeGyp = (0, path_1.join)(__dirname, '../node_modules/.bin/node-gyp');
153
+ await (0, common_1.loudExec)(nodeGyp, ['rebuild'], { cwd: gypDir });
154
+ const built = (0, path_1.join)(gypDir, 'build/Release/zano.node');
155
+ await (0, common_1.loudExec)('cp', [built, (0, path_1.join)(prebuildDir, 'zano.node')]);
156
+ // Remove local and debug symbols. The addon is loaded through its
157
+ // registration entry point, which is global and survives `-x`.
158
+ await (0, common_1.loudExec)('strip', ['-x', (0, path_1.join)(prebuildDir, 'zano.node')]);
159
+ console.log(`Wrote ${(0, path_1.join)(prebuildDir, 'zano.node')}`);
160
+ }
161
+ async function main() {
162
+ if (process.platform !== 'darwin') {
163
+ throw new Error(`Host Zano builds are currently only supported on darwin (got ${process.platform}). ` +
164
+ 'Linux would need _install_linux plain-wallet libs from zano_native_lib.');
165
+ }
166
+ const arch = process.arch === 'arm64' ? 'arm64' : 'x86_64';
167
+ await (0, promises_1.mkdir)(common_1.tmpPath, { recursive: true });
168
+ await downloadSources();
169
+ const staticLib = await buildHostStaticLib(arch);
170
+ await buildNodeAddon(staticLib);
171
+ }
172
+ main().catch((error) => {
173
+ console.error(error);
174
+ process.exit(1);
175
+ });
@@ -0,0 +1 @@
1
+ export {};