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.
- package/CHANGELOG.md +90 -0
- package/LICENSE +27 -0
- package/README.md +24 -0
- package/android/build.gradle +40 -0
- package/android/src/main/java/app/edge/rnzano/RnZanoModule.java +73 -0
- package/android/src/main/java/app/edge/rnzano/RnZanoPackage.java +21 -0
- package/android/src/main/jniLibs/arm64-v8a/librnzano.so +0 -0
- package/ios/ZanoModule.h +4 -0
- package/ios/ZanoModule.mm +138 -0
- package/ios/ZanoModule.xcframework/Info.plist +43 -0
- package/ios/ZanoModule.xcframework/ios-arm64/libzano-module.a +0 -0
- package/ios/ZanoModule.xcframework/ios-arm64-simulator/libzano-module.a +0 -0
- package/ios/react-native-zano.xcodeproj/project.pbxproj +1 -0
- package/lib/scripts/build-native-host.d.ts +1 -0
- package/lib/scripts/build-native-host.js +175 -0
- package/lib/scripts/smoke-node.d.ts +1 -0
- package/lib/scripts/smoke-node.js +33 -0
- package/lib/scripts/update-sources.d.ts +1 -0
- package/lib/scripts/update-sources.js +412 -0
- package/lib/scripts/utils/android-tools.d.ts +1 -0
- package/lib/scripts/utils/android-tools.js +25 -0
- package/lib/scripts/utils/closeWalletPatch.d.ts +23 -0
- package/lib/scripts/utils/closeWalletPatch.js +215 -0
- package/lib/scripts/utils/common.d.ts +37 -0
- package/lib/scripts/utils/common.js +186 -0
- package/lib/scripts/utils/ios-tools.d.ts +8 -0
- package/lib/scripts/utils/ios-tools.js +26 -0
- package/lib/scripts/utils/sdkFolders.d.ts +27 -0
- package/lib/scripts/utils/sdkFolders.js +43 -0
- package/lib/src/CppBridge.d.ts +142 -0
- package/lib/src/CppBridge.js +668 -0
- package/lib/src/index.d.ts +4 -0
- package/lib/src/index.js +28 -0
- package/lib/src/load-addon.d.ts +5 -0
- package/lib/src/load-addon.js +50 -0
- package/lib/src/node.d.ts +11 -0
- package/lib/src/node.js +25 -0
- package/lib/src/types.d.ts +292 -0
- package/lib/src/types.js +40 -0
- package/lib/src/walletFilePassword.d.ts +14 -0
- package/lib/src/walletFilePassword.js +74 -0
- package/node.d.ts +5 -0
- package/node.js +2 -0
- package/package.json +105 -0
- package/prebuilds/darwin-arm64/zano.node +0 -0
- package/src/node/zano-napi.cpp +176 -0
- package/src/zano-wrapper/zano-methods.hpp +15 -0
- 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
|
+
}
|
|
Binary file
|
package/ios/ZanoModule.h
ADDED
|
@@ -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>
|
|
Binary file
|
|
Binary file
|
|
@@ -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 {};
|