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
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ // Smoke-test the Node addon: hello + getVersion.
3
+ // Run after `npm run build-native-host`:
4
+ // node -r sucrase/register ./scripts/smoke-node.ts
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = require("fs");
7
+ const os_1 = require("os");
8
+ const path_1 = require("path");
9
+ const node_1 = require("../src/node");
10
+ async function main() {
11
+ const documentDirectory = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'zano-node-'));
12
+ const module = (0, node_1.makeNodeZanoModule)({ documentDirectory });
13
+ const hello = await module.callZano('hello', []);
14
+ console.log('hello:', hello);
15
+ const version = await module.callZano('getVersion', []);
16
+ console.log('getVersion:', version);
17
+ const names = module.methodNames;
18
+ console.log('methodNames count:', names.length);
19
+ if (!names.includes('hello') || !names.includes('getVersion')) {
20
+ throw new Error('methodNames missing hello/getVersion');
21
+ }
22
+ if (hello !== 'hello') {
23
+ throw new Error(`unexpected hello result: ${hello}`);
24
+ }
25
+ if (version.length === 0) {
26
+ throw new Error('getVersion returned empty string');
27
+ }
28
+ process.exit(0);
29
+ }
30
+ main().catch((error) => {
31
+ console.error(error);
32
+ process.exit(1);
33
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,412 @@
1
+ "use strict";
2
+ // Run this script as `node -r sucrase/register ./scripts/update-sources.ts`
3
+ //
4
+ // It will:
5
+ // - Download third-party source code.
6
+ // - Assemble Android shared libraries for each platform.
7
+ // - Assemble an iOS universal static xcframework.
8
+ //
9
+ // Here is where each puzzle piece comes from:
10
+ //
11
+ // | | Zano | OpenSSL | Boost |
12
+ // |---------|-------|-------------------|-------------------|
13
+ // | Android | CMake | zano_native_lib | Boost-for-Android |
14
+ // | iOS | CMake | OpenSSL-Universal | zano_native_lib |
15
+ //
16
+ // For both iOS and Android, we build Zano by invoking CMake directly.
17
+ // The zano_native_lib build scripts aren't really useful,
18
+ // since the aren't geared towards React Native auto-linking.
19
+ // Calling CMake ourselves isn't that hard.
20
+ //
21
+ // On Android, the zano_native_lib OpenSSL libraries in are fine,
22
+ // but we have to use Boost-for-Android to get the right STL version.
23
+ //
24
+ // On iOS, the boost libraries are fine,
25
+ // but their OpenSSL is just a re-packaged copy OpenSSL-Universal,
26
+ // so it's simpler to pull that in from CocoaPods directly.
27
+ //
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const promises_1 = require("fs/promises");
30
+ const os_1 = require("os");
31
+ const path_1 = require("path");
32
+ const android_tools_1 = require("./utils/android-tools");
33
+ const closeWalletPatch_1 = require("./utils/closeWalletPatch");
34
+ const common_1 = require("./utils/common");
35
+ const ios_tools_1 = require("./utils/ios-tools");
36
+ const sdkFolders_1 = require("./utils/sdkFolders");
37
+ const srcPath = (0, path_1.join)(__dirname, '../src');
38
+ async function main() {
39
+ await (0, promises_1.mkdir)(common_1.tmpPath, { recursive: true });
40
+ await downloadSources();
41
+ await checkSdkFolders();
42
+ // Android:
43
+ await buildAndroidBoost();
44
+ for (const platform of androidPlatforms) {
45
+ await buildAndroidZano(platform);
46
+ }
47
+ // iOS:
48
+ for (const platform of iosPlatforms) {
49
+ await buildIosZano(platform);
50
+ }
51
+ await packageIosZano();
52
+ }
53
+ async function downloadSources() {
54
+ await (0, common_1.getRepo)('zano_native_lib', 'https://github.com/hyle-team/zano_native_lib.git', '91085c0ebd95fcdae3327071a9e5f5b615d7da3d',
55
+ // The repo keeps prebuilt archives for every platform in Git LFS,
56
+ // but we only link against these two:
57
+ { lfsIncludes: ['_install_ios/**', '_libs_android/**'] });
58
+ await (0, common_1.getRepo)('Boost-for-Android', 'https://github.com/moritz-wundke/Boost-for-Android.git', '51924ec5533a4fefb5edf99feaeded794c06a4fb');
59
+ // Rename the compress function in RIPEMD160.c,
60
+ // since that conflicts with Zlib:
61
+ const mdPath = (0, path_1.join)(common_1.tmpPath, 'zano_native_lib/Zano/src/crypto/RIPEMD160.h');
62
+ const mdText = await (0, promises_1.readFile)(mdPath, 'utf8');
63
+ await (0, promises_1.writeFile)(mdPath, '#define compress md_compress\n' + mdText);
64
+ // Rework close_wallet so it does not hold the wallet-manager lock while
65
+ // it waits on the wallet, which permanently deadlocks the manager when a
66
+ // wallet is closed mid-scan (see closeWalletPatch.ts). Only the Android
67
+ // libraries build from these sources; iOS links the prebuilt framework:
68
+ const wmPath = (0, path_1.join)(common_1.tmpPath, 'zano_native_lib/Zano/src/wallet/wallets_manager.cpp');
69
+ const wmText = await (0, promises_1.readFile)(wmPath, 'utf8');
70
+ await (0, promises_1.writeFile)(wmPath, (0, closeWalletPatch_1.patchCloseWallet)(wmText));
71
+ }
72
+ /**
73
+ * Fails the build if the SDK declares a directory we do not know about.
74
+ *
75
+ * The SDK writes into directories it derives from the working directory the
76
+ * app hands it, and on iOS those have to be pre-created so they can be
77
+ * excluded from device backups -- the wallet files hold the seed and spend
78
+ * keys. That list lives in `ios/ZanoModule.mm` and cannot be derived at
79
+ * build time, so it has to be maintained by hand.
80
+ *
81
+ * Bumping the `zano_native_lib` pin is the only way a new directory can
82
+ * appear, and this script is what performs that bump, so this is the one
83
+ * place that always sees the new source. Without this check a new directory
84
+ * would ship silently, un-excluded, with nothing in the repo's diff to show
85
+ * for it -- `tmp/` is gitignored, so a pin bump reviews as a changed hash.
86
+ *
87
+ * This catches directories declared as `#define`d folder names, which is how
88
+ * the SDK has always declared them. It cannot catch a path composed inline,
89
+ * so it is a tripwire rather than a proof.
90
+ */
91
+ async function checkSdkFolders() {
92
+ const apiPath = (0, path_1.join)(common_1.tmpPath, 'zano_native_lib/Zano/src/wallet/plain_wallet_api.cpp');
93
+ const apiText = await (0, promises_1.readFile)(apiPath, 'utf8');
94
+ const found = (0, sdkFolders_1.findSdkFolders)(apiText);
95
+ const missing = found.filter(name => !sdkFolders_1.sdkFolders.includes(name));
96
+ if (missing.length > 0) {
97
+ throw new Error(`The Zano SDK declares directories this package does not handle: ` +
98
+ `${missing.join(', ')}. Add them to \`sdkFolders\` here and to the ` +
99
+ '`prepareZanoDirectory` calls in `ios/ZanoModule.mm`, so they are ' +
100
+ 'created and excluded from device backups.');
101
+ }
102
+ const extra = sdkFolders_1.sdkFolders.filter(name => !found.includes(name));
103
+ if (extra.length > 0) {
104
+ // Not fatal, since an unused directory is harmless. It usually means the
105
+ // SDK renamed something, in which case the new name failed above.
106
+ console.log(`Note: the SDK no longer declares ${extra.join(', ')}. ` +
107
+ 'These can probably be dropped from `sdkFolders`.');
108
+ }
109
+ console.log(`Checked SDK directories: ${found.join(', ')}`);
110
+ }
111
+ // Compiler options:
112
+ const includePaths = ['zano_native_lib/Zano/src/wallet'];
113
+ // Source list (from src/):
114
+ const sources = ['zano-wrapper/zano-methods.cpp'];
115
+ // .a files from Zano:
116
+ const zanoLibs = ['common', 'crypto', 'currency_core', 'wallet', 'z'];
117
+ // .a files from boost:
118
+ const boostLibs = [
119
+ 'atomic',
120
+ 'chrono',
121
+ 'date_time',
122
+ 'filesystem',
123
+ 'program_options',
124
+ 'regex',
125
+ 'serialization',
126
+ 'system',
127
+ 'thread',
128
+ 'timer'
129
+ ];
130
+ const androidPlatforms = [
131
+ { arch: 'arm64-v8a', triple: 'aarch64-linux-android33' },
132
+ { arch: 'armeabi-v7a', triple: 'armv7a-linux-androideabi23' },
133
+ { arch: 'x86', triple: 'i686-linux-android23' },
134
+ { arch: 'x86_64', triple: 'x86_64-linux-android23' }
135
+ ];
136
+ // Phones and simulators we need to support:
137
+ const iosPlatforms = [
138
+ { sdk: 'iphoneos', arch: 'arm64', cmakePlatform: 'OS64' },
139
+ { sdk: 'iphonesimulator', arch: 'arm64', cmakePlatform: 'SIMULATORARM64' },
140
+ { sdk: 'iphonesimulator', arch: 'x86_64', cmakePlatform: 'SIMULATOR64' }
141
+ // Zano does not support these:
142
+ // { sdk: 'iphoneos', arch: 'armv7' },
143
+ // { sdk: 'iphoneos', arch: 'armv7s' },
144
+ ];
145
+ /**
146
+ * `--arm64` narrows the build to the slices a modern phone, an Apple-Silicon
147
+ * simulator and the Node CLI actually need: one Android ABI and the two arm64
148
+ * iOS slices, instead of four ABIs and an Intel simulator. Filtering the
149
+ * shared arrays in place keeps every consumer in agreement about what exists
150
+ * — the build loops, Boost's `--arch` list, and the xcframework packaging,
151
+ * which would otherwise `lipo` a slice that was never built.
152
+ */
153
+ if (process.argv.includes('--arm64')) {
154
+ androidPlatforms.splice(0, androidPlatforms.length, ...androidPlatforms.filter(platform => platform.arch === 'arm64-v8a'));
155
+ iosPlatforms.splice(0, iosPlatforms.length, ...iosPlatforms.filter(platform => platform.arch === 'arm64'));
156
+ }
157
+ const iosSdkTriples = {
158
+ iphoneos: '%arch%-apple-ios13.0',
159
+ iphonesimulator: '%arch%-apple-ios13.0-simulator'
160
+ };
161
+ /**
162
+ * We compile our own Boost for Android,
163
+ * because the Zano one is linking with the static STL library,
164
+ * but we need to link with the shared STL library.
165
+ */
166
+ async function buildAndroidBoost() {
167
+ const boostExists = await (0, common_1.fileExists)((0, path_1.join)(common_1.tmpPath, 'Boost-for-Android/build/out/arm64-v8a/lib/libboost_atomic.a'));
168
+ if (boostExists)
169
+ return;
170
+ const ndkPath = await (0, android_tools_1.getNdkPath)();
171
+ await (0, common_1.loudExec)('./build-android.sh', [
172
+ `--arch=${androidPlatforms.map(platform => platform.arch).join(',')}`,
173
+ '--boost=1.84.0',
174
+ `--with-libraries=${boostLibs.join(',')}`,
175
+ '--layout=system',
176
+ ndkPath
177
+ ], { cwd: (0, path_1.join)(common_1.tmpPath, 'Boost-for-Android') });
178
+ }
179
+ /**
180
+ * Invokes CMake to build Zano,
181
+ * followed by clang++ to build the shared library.
182
+ */
183
+ async function buildAndroidZano(platform) {
184
+ const { arch, triple } = platform;
185
+ const ndkPath = await (0, android_tools_1.getNdkPath)();
186
+ const working = (0, path_1.join)(common_1.tmpPath, `android-${arch}`);
187
+ const boostPath = (0, path_1.join)(common_1.tmpPath, 'Boost-for-Android/build/out/', arch);
188
+ const sslPath = (0, path_1.join)(common_1.tmpPath, 'zano_native_lib/_libs_android/openssl');
189
+ const zanoLibPath = (0, path_1.join)(common_1.tmpPath, `android-${arch}/${arch}/lib/`);
190
+ // Build Zano itself:
191
+ await (0, common_1.loudExec)('cmake', [
192
+ // Source directory:
193
+ `-S${(0, path_1.join)(common_1.tmpPath, 'zano_native_lib/Zano')}`,
194
+ // Build directory:
195
+ `-B${(0, path_1.join)(working, 'cmake')}`,
196
+ // Build options:
197
+ `-DBoost_INCLUDE_DIRS=${(0, path_1.join)(boostPath, 'include')}`,
198
+ `-DBoost_LIBRARY_DIRS=${(0, path_1.join)(boostPath, 'lib')}`,
199
+ `-DBoost_VERSION="1.84.0"`,
200
+ `-DCMAKE_ANDROID_ARCH_ABI=${arch}`,
201
+ `-DCMAKE_ANDROID_NDK=${ndkPath}`,
202
+ `-DCMAKE_ANDROID_STL_TYPE=c++_shared`,
203
+ `-DCMAKE_BUILD_TYPE=Release`,
204
+ `-DCMAKE_INSTALL_PREFIX=${working}`,
205
+ `-DCMAKE_SYSTEM_NAME=Android`,
206
+ `-DCMAKE_SYSTEM_VERSION=23`,
207
+ `-DDISABLE_TOR=TRUE`,
208
+ `-DOPENSSL_CRYPTO_LIBRARY=${(0, path_1.join)(sslPath, arch, 'lib/libcrypto.a')}`,
209
+ `-DOPENSSL_INCLUDE_DIR=${(0, path_1.join)(sslPath, 'include')}`,
210
+ `-DOPENSSL_SSL_LIBRARY=${(0, path_1.join)(sslPath, arch, 'lib/libssl.a')}`
211
+ ]);
212
+ await (0, common_1.loudExec)('cmake', [
213
+ '--build',
214
+ (0, path_1.join)(working, 'cmake'),
215
+ '--config',
216
+ 'Release',
217
+ '--target',
218
+ 'install',
219
+ '--',
220
+ `-j${(0, os_1.cpus)().length}`
221
+ ]);
222
+ // Build the library:
223
+ const cxxPath = (0, path_1.join)(ndkPath, `toolchains/llvm/prebuilt/darwin-x86_64/bin/${triple}-clang++`);
224
+ const outPath = (0, path_1.join)(common_1.tmpPath, '../android/src/main/jniLibs/', arch);
225
+ await (0, promises_1.mkdir)(outPath, { recursive: true });
226
+ const jniSources = [...sources, 'jni/jni.cpp'];
227
+ const sslLibs = ['crypto', 'ssl'];
228
+ console.log(`Linking librnzano.so for Android ${arch}`);
229
+ await (0, common_1.loudExec)(cxxPath, [
230
+ '-shared',
231
+ '-fPIC',
232
+ `-o${(0, path_1.join)(outPath, 'librnzano.so')}`,
233
+ ...includePaths.map(path => `-I${(0, path_1.join)(common_1.tmpPath, path)}`),
234
+ ...jniSources.map(source => (0, path_1.join)(srcPath, source)),
235
+ ...boostLibs.map(name => (0, path_1.join)(boostPath, `/lib/libboost_${name}.a`)),
236
+ ...sslLibs.map(name => (0, path_1.join)(sslPath, `${arch}/lib/lib${name}.a`)),
237
+ ...zanoLibs.map(name => (0, path_1.join)(zanoLibPath, `lib${name}.a`)),
238
+ '-llog',
239
+ `-Wl,--version-script=${(0, path_1.join)(srcPath, 'jni/exports.map')}`,
240
+ '-Wl,--no-undefined',
241
+ '-Wl,-z,max-page-size=16384',
242
+ // Drop the symbol table. JNI resolves through .dynsym, which
243
+ // --strip-all keeps, and the debug symbols roughly double the shipped
244
+ // library otherwise.
245
+ '-Wl,--strip-all'
246
+ ]);
247
+ }
248
+ /**
249
+ * Locates the slice of a prebuilt xcframework that matches one platform.
250
+ *
251
+ * An xcframework declares its own slices in `Info.plist`, so ask it rather
252
+ * than hardcoding directory names: a repackaged upstream framework then
253
+ * fails here with a clear message instead of further down with a missing
254
+ * file.
255
+ *
256
+ * The returned archive is checked against the architecture we are building.
257
+ * The simulator slice holds both architectures in one archive, so the slice
258
+ * alone does not pin the arch -- only the `-arch` flag on the relocatable
259
+ * link below does, and Apple's `ld` tends to warn rather than fail when an
260
+ * archive member does not match it. This also catches an unfetched Git LFS
261
+ * pointer sitting where the 150MB archive should be, which otherwise shows
262
+ * up as a confusing link error.
263
+ */
264
+ async function findXcframeworkSlice(frameworkPath, platform) {
265
+ const { arch, sdk } = platform;
266
+ const variant = sdk === 'iphonesimulator' ? 'simulator' : undefined;
267
+ const plist = JSON.parse(await (0, common_1.captureExec)('plutil', [
268
+ '-convert',
269
+ 'json',
270
+ '-o',
271
+ '-',
272
+ (0, path_1.join)(frameworkPath, 'Info.plist')
273
+ ]));
274
+ const slice = plist.AvailableLibraries.find(library => library.SupportedPlatform === 'ios' &&
275
+ library.SupportedPlatformVariant === variant &&
276
+ library.SupportedArchitectures.includes(arch));
277
+ if (slice == null) {
278
+ throw new Error(`${frameworkPath} has no ios${variant == null ? '' : `-${variant}`} slice for ${arch}. It offers: ${plist.AvailableLibraries.map(library => library.LibraryIdentifier).join(', ')}`);
279
+ }
280
+ const slicePath = (0, path_1.join)(frameworkPath, slice.LibraryIdentifier);
281
+ const libraryPath = (0, path_1.join)(slicePath, slice.LibraryPath);
282
+ const archs = (await (0, common_1.captureExec)('lipo', ['-archs', libraryPath])).split(/\s+/);
283
+ if (!archs.includes(arch)) {
284
+ throw new Error(`${libraryPath} holds ${archs.join(', ')}, but we are building ${arch}`);
285
+ }
286
+ return {
287
+ headersPath: (0, path_1.join)(slicePath, slice.HeadersPath ?? 'Headers'),
288
+ libraryPath
289
+ };
290
+ }
291
+ /**
292
+ * Compiles our wrapper and links it against the prebuilt
293
+ * libzano-plain-wallet static library (which already bundles Zano,
294
+ * Boost, and OpenSSL), then localizes symbols into a static lib.
295
+ *
296
+ * As of zano_native_lib HF6, the repo no longer ships the raw
297
+ * `_libs_ios` OpenSSL/Boost archives we used to build Zano from
298
+ * source against. Instead it provides prebuilt xcframeworks, so we
299
+ * link the plain-wallet bundle directly.
300
+ */
301
+ async function buildIosZano(platform) {
302
+ const { sdk, arch } = platform;
303
+ const working = (0, path_1.join)(common_1.tmpPath, `${sdk}-${arch}`);
304
+ await (0, promises_1.mkdir)(working, { recursive: true });
305
+ // The prebuilt plain-wallet xcframework slice for this platform:
306
+ const { headersPath, libraryPath: zanoLib } = await findXcframeworkSlice((0, path_1.join)(common_1.tmpPath, 'zano_native_lib/_install_ios/lib/libzano-plain-wallet.xcframework'), platform);
307
+ // Find platform tools:
308
+ const ar = await (0, common_1.quietExec)('xcrun', ['--sdk', sdk, '--find', 'ar']);
309
+ const cc = await (0, common_1.quietExec)('xcrun', ['--sdk', sdk, '--find', 'clang']);
310
+ const cxx = await (0, common_1.quietExec)('xcrun', ['--sdk', sdk, '--find', 'clang++']);
311
+ const ld = await (0, common_1.quietExec)('xcrun', ['--sdk', sdk, '--find', 'ld']);
312
+ const objcopy = await (0, ios_tools_1.getObjcopyPath)();
313
+ const sdkFlags = [
314
+ '-arch',
315
+ arch,
316
+ '-target',
317
+ iosSdkTriples[sdk].replace('%arch%', arch),
318
+ '-isysroot',
319
+ await (0, common_1.quietExec)('xcrun', ['--sdk', sdk, '--show-sdk-path'])
320
+ ];
321
+ const cflags = [
322
+ `-I${headersPath}`,
323
+ '-miphoneos-version-min=13.0',
324
+ '-O2',
325
+ '-Werror=partial-availability'
326
+ ];
327
+ const cxxflags = [...cflags, '-std=c++11'];
328
+ // Compile our sources:
329
+ const objects = [];
330
+ for (const source of sources) {
331
+ console.log(`Compiling ${source} for ${sdk}-${arch}...`);
332
+ // Figure out the object file name:
333
+ const object = (0, path_1.join)(working, source.replace(/^.*\//, '').replace(/\.c$|\.cc$|\.cpp$/, '.o'));
334
+ objects.push(object);
335
+ const useCxx = /\.cpp$|\.cc$/.test(source);
336
+ await (0, common_1.loudExec)(useCxx ? cxx : cc, [
337
+ '-c',
338
+ ...(useCxx ? cxxflags : cflags),
339
+ ...sdkFlags,
340
+ `-o${object}`,
341
+ (0, path_1.join)(srcPath, source)
342
+ ]);
343
+ }
344
+ // Link our wrapper against the prebuilt plain-wallet library
345
+ // into a single relocatable object. `ld -r` pulls in only the
346
+ // archive members our wrapper transitively needs:
347
+ console.log(`Linking zano-module.o for ${sdk} ${arch}`);
348
+ const objectPath = (0, path_1.join)(working, 'zano-module.o');
349
+ await (0, common_1.loudExec)(ld, [
350
+ '-r',
351
+ '-arch',
352
+ arch,
353
+ '-o',
354
+ objectPath,
355
+ ...objects,
356
+ zanoLib
357
+ ]);
358
+ // Localize all symbols except the ones we really want,
359
+ // hiding them from future linking steps:
360
+ await (0, common_1.loudExec)(objcopy, [
361
+ objectPath,
362
+ '-w',
363
+ '-L*',
364
+ '-L!_zanoMethods',
365
+ '-L!_zanoMethodCount'
366
+ ]);
367
+ // Generate a static library:
368
+ console.log(`Building static library for ${sdk}-${arch}...`);
369
+ const library = (0, path_1.join)(working, `libzano-module.a`);
370
+ await (0, promises_1.rm)(library, { force: true });
371
+ await (0, common_1.loudExec)(ar, ['rcs', library, objectPath]);
372
+ }
373
+ /**
374
+ * Creates a unified xcframework file out of the per-platform
375
+ * static libraries that `buildIosZano` creates.
376
+ */
377
+ async function packageIosZano() {
378
+ const sdks = new Set(iosPlatforms.map(row => row.sdk));
379
+ // Merge the platforms into a fat library:
380
+ const merged = [];
381
+ for (const sdk of sdks) {
382
+ console.log(`Merging libraries for ${sdk}...`);
383
+ const outPath = (0, path_1.join)(common_1.tmpPath, `${sdk}-lipo`);
384
+ await (0, promises_1.mkdir)(outPath, { recursive: true });
385
+ const output = (0, path_1.join)(outPath, 'libzano-module.a');
386
+ await (0, common_1.loudExec)('lipo', [
387
+ '-create',
388
+ '-output',
389
+ output,
390
+ ...iosPlatforms
391
+ .filter(platform => platform.sdk === sdk)
392
+ .map(({ sdk, arch }) => (0, path_1.join)(common_1.tmpPath, `${sdk}-${arch}`, `libzano-module.a`))
393
+ ]);
394
+ merged.push('-library', output);
395
+ }
396
+ // Bundle those into an XCFramework:
397
+ console.log('Creating XCFramework...');
398
+ await (0, promises_1.rm)('ios/ZanoModule.xcframework', { recursive: true, force: true });
399
+ await (0, common_1.loudExec)('xcodebuild', [
400
+ '-create-xcframework',
401
+ ...merged,
402
+ '-output',
403
+ (0, path_1.join)(__dirname, '../ios/ZanoModule.xcframework')
404
+ ]);
405
+ }
406
+ main().catch(error => {
407
+ // This is the `prepack` script, so a swallowed failure means `npm publish`
408
+ // reports success against whatever stale `ios/ZanoModule.xcframework` was
409
+ // lying around, and no check this script makes can ever fail a build.
410
+ console.error(error);
411
+ process.exitCode = 1;
412
+ });
@@ -0,0 +1 @@
1
+ export declare function getNdkPath(): Promise<string>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNdkPath = void 0;
4
+ const path_1 = require("path");
5
+ const common_1 = require("./common");
6
+ // Matches the Edge app NDK version
7
+ // The Zano build used '27.2.12479018', so we could try upgrading.
8
+ const NDK_VERSION = '26.1.10909125';
9
+ async function getNdkPath() {
10
+ const { ANDROID_HOME } = process.env;
11
+ if (ANDROID_HOME == null) {
12
+ throw new Error('ANDROID_HOME is not set in the environment.');
13
+ }
14
+ // Find the NDK:
15
+ const ndkPath = (0, path_1.join)(ANDROID_HOME, 'ndk', NDK_VERSION);
16
+ const hasNdk = await (0, common_1.fileExists)(ndkPath);
17
+ // Install the NDK if we need it:
18
+ if (!hasNdk) {
19
+ console.log(`Installing NDK ${NDK_VERSION}...`);
20
+ const sdkManagerPath = (0, path_1.join)(ANDROID_HOME, 'cmdline-tools/latest/bin/sdkmanager');
21
+ await (0, common_1.loudExec)(sdkManagerPath, [`"ndk;${NDK_VERSION}"`]);
22
+ }
23
+ return ndkPath;
24
+ }
25
+ exports.getNdkPath = getNdkPath;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Rewrites the SDK's `wallets_manager::close_wallet` so it does not hold
3
+ * the wallet-manager lock while waiting on the wallet being closed. See
4
+ * the comment on `replacement` above for the deadlock this removes and
5
+ * the semantics it deliberately changes.
6
+ *
7
+ * Only the Android libraries pick this up: iOS links the prebuilt
8
+ * `libzano-plain-wallet` xcframework rather than building these sources,
9
+ * and iOS runs the same close-during-catch-up cycle without wedging.
10
+ *
11
+ * The function is located by its unique signature, delimited by brace
12
+ * counting, and then required to match the pinned original exactly
13
+ * (modulo trailing whitespace), so a pin bump that changes `close_wallet`
14
+ * in any way fails the build here instead of silently keeping (or
15
+ * dropping) a stale patch. The brace counter would be fooled by a brace
16
+ * inside a string literal, but the full-body comparison catches that case
17
+ * too: a mis-delimited body cannot match the original.
18
+ *
19
+ * @param text - The contents of the SDK's `wallets_manager.cpp`.
20
+ * @returns The patched contents. Already-patched input comes back
21
+ * unchanged, so the caller does not need to track whether it ran.
22
+ */
23
+ export declare function patchCloseWallet(text: string): string;