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,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patchCloseWallet = void 0;
4
+ const patchMarker = 'Edge patch: close-during-refresh deadlock';
5
+ const hint = 'The SDK sources changed at this pin. If upstream has fixed the ' +
6
+ 'close-during-refresh lock inversion, delete this patch; otherwise port ' +
7
+ 'it to the new body.';
8
+ /**
9
+ * `close_wallet` as pinned at zano_native_lib 91085c0, modulo invisible
10
+ * trailing whitespace. The transform refuses to run unless the function it
11
+ * found matches this byte-for-byte after trailing whitespace is stripped,
12
+ * so ANY upstream drift - not just drift through the lines the deadlock
13
+ * analysis rests on - fails the build for a human to re-evaluate.
14
+ */
15
+ const original = `std::string wallets_manager::close_wallet(size_t wallet_id)
16
+ {
17
+ EXCLUSIVE_CRITICAL_REGION_LOCAL(m_wallets_lock);
18
+
19
+ auto it = m_wallets.find(wallet_id);
20
+ if (it == m_wallets.end())
21
+ return API_RETURN_CODE_WALLET_WRONG_ID;
22
+
23
+
24
+ try
25
+ {
26
+ it->second.major_stop = true;
27
+ it->second.stop_for_refresh = true;
28
+ it->second.w.unlocked_get()->stop();
29
+
30
+ it->second.w->get()->store();
31
+ m_wallets.erase(it);
32
+ {
33
+ CRITICAL_REGION_LOCAL(m_wallet_log_prefixes_lock);
34
+ m_wallet_log_prefixes[wallet_id] = std::string("[") + epee::string_tools::num_to_string_fast(wallet_id) + ":CLOSED] ";
35
+ }
36
+ }
37
+
38
+ catch (const std::exception& e)
39
+ {
40
+ return std::string(API_RETURN_CODE_FAIL) + ":" + e.what();
41
+ }
42
+ catch (...)
43
+ {
44
+ return API_RETURN_CODE_INTERNAL_ERROR;
45
+ }
46
+ //m_pview->hide_wallet();
47
+ return API_RETURN_CODE_OK;
48
+ }`;
49
+ /**
50
+ * The replacement `close_wallet`. The pinned original holds
51
+ * `m_wallets_lock` exclusively across two waits on the wallet being
52
+ * closed: the `store()` call, which needs the per-wallet lock the refresh
53
+ * worker holds for the whole of a scan chunk, and the map erase, whose
54
+ * destructor joins the worker thread. The refresh path re-enters the
55
+ * manager through wallet callbacks (`on_transfer2`, `on_transfer_canceled`)
56
+ * that take `m_wallets_lock` shared, and every other API call takes it
57
+ * shared up front - so a close issued while the wallet is catching up
58
+ * wedges the worker, the close, and then every Zano call in the process,
59
+ * permanently. Thread dumps of the frozen app show the close parked inside
60
+ * the native call, and no checkpoint ever lands again.
61
+ *
62
+ * The rewrite detaches the map node while holding the lock, then does the
63
+ * slow parts - the store and the implicit thread join when the node dies
64
+ * - with no manager lock held at all. Node extraction keeps the element
65
+ * at its address, so the worker thread's references stay valid; wallet ids
66
+ * are never reused, so late callbacks cannot hit a recycled id. The
67
+ * log-prefix write gains a bounds check, because the manager lock no
68
+ * longer serializes it against `reset()`'s vector clear.
69
+ *
70
+ * Deliberate semantics changes, all confined to the window after the node
71
+ * leaves the map:
72
+ *
73
+ * - "Absent from `m_wallets`" no longer implies "closed and stored".
74
+ * During the store/join window the wallet is invisible to
75
+ * `open_wallet`'s ALREADY_EXISTS check and to status calls while its
76
+ * `wallet2` still writes the file. Safe for this bridge, which strictly
77
+ * sequences close-before-reopen per wallet on one executor thread; a
78
+ * caller without that discipline could double-open the file. Say so in
79
+ * any upstream submission.
80
+ * - The refresh worker's own callbacks miss where they used to hit. Both
81
+ * `on_transfer2` and `on_transfer_canceled` look the wallet up by id
82
+ * under a shared `m_wallets_lock` while the store runs, and the entry is
83
+ * no longer there. Both lookups are checked - `on_transfer2` through
84
+ * `GET_WALLET_OPTIONS_BY_ID_VOID_RET`, which returns on a miss, and
85
+ * `on_transfer_canceled` with an explicit `end()` test that logs and
86
+ * returns - so the cost is a dropped view notification for a wallet
87
+ * that is closing anyway, not an unchecked dereference. Every
88
+ * `m_wallets.find` in the file is checked this way; the three bare
89
+ * `m_wallets[...]` sites all insert a freshly counted id in the open,
90
+ * restore and generate paths, and ids are never reused.
91
+ * - When `store()` throws, the original left the entry in the map as a
92
+ * zombie (its stop flags already set, so it could never sync again);
93
+ * the rewrite reports the same error but the wallet is gone. A
94
+ * `closeWallet` retry therefore reports WALLET_WRONG_ID instead of
95
+ * retrying the store, so `CppBridge`'s re-key migration takes its
96
+ * "leave the file alone" branch with the wallet already released; the
97
+ * next launch retries the migration.
98
+ *
99
+ * iOS links the prebuilt `libzano-plain-wallet` framework and keeps the
100
+ * original blocking semantics throughout.
101
+ */
102
+ const replacement = `std::string wallets_manager::close_wallet(size_t wallet_id)
103
+ {
104
+ // ${patchMarker}.
105
+ //
106
+ // The original held m_wallets_lock exclusively across store() and across
107
+ // the map erase, whose destructor joins the refresh worker. Both wait on
108
+ // a wallet that may be mid-refresh, and the refresh path re-enters the
109
+ // manager through wallet callbacks that take m_wallets_lock shared - so
110
+ // a close issued during a long refresh held the very lock the worker
111
+ // needed to reach its stop flags, and neither side could ever proceed.
112
+ // Detach the map node under the lock instead, then store and join with
113
+ // no manager lock held. The node is declared outside the try so its
114
+ // destructor - which joins the worker thread the stop flags told to
115
+ // finish - runs at function exit rather than during unwinding, while
116
+ // everything that can throw stays inside the try, so failures keep
117
+ // reporting as return codes exactly as they did before this patch.
118
+ decltype(m_wallets)::node_type wallet_node;
119
+ try
120
+ {
121
+ {
122
+ EXCLUSIVE_CRITICAL_REGION_LOCAL(m_wallets_lock);
123
+
124
+ auto it = m_wallets.find(wallet_id);
125
+ if (it == m_wallets.end())
126
+ return API_RETURN_CODE_WALLET_WRONG_ID;
127
+
128
+ it->second.major_stop = true;
129
+ it->second.stop_for_refresh = true;
130
+ it->second.w.unlocked_get()->stop();
131
+ wallet_node = m_wallets.extract(it);
132
+ }
133
+
134
+ wallet_node.mapped().w->get()->store();
135
+ {
136
+ CRITICAL_REGION_LOCAL(m_wallet_log_prefixes_lock);
137
+ // The manager lock no longer serializes this write against reset()'s
138
+ // clear of the vector, so respect its current size:
139
+ if (wallet_id < m_wallet_log_prefixes.size())
140
+ m_wallet_log_prefixes[wallet_id] = std::string("[") + epee::string_tools::num_to_string_fast(wallet_id) + ":CLOSED] ";
141
+ }
142
+ }
143
+ catch (const std::exception& e)
144
+ {
145
+ return std::string(API_RETURN_CODE_FAIL) + ":" + e.what();
146
+ }
147
+ catch (...)
148
+ {
149
+ return API_RETURN_CODE_INTERNAL_ERROR;
150
+ }
151
+ //m_pview->hide_wallet();
152
+ return API_RETURN_CODE_OK;
153
+ }`;
154
+ /** Strips trailing whitespace per line, the one formatting freedom the
155
+ * comparison allows - the pinned file carries an invisible trailing
156
+ * space that transcriptions of it should not have to reproduce. `\r` is
157
+ * in the class so a CRLF checkout reports the drift it has, rather than
158
+ * one carriage return per line. */
159
+ function normalize(code) {
160
+ return code
161
+ .split('\n')
162
+ .map(line => line.replace(/[ \t\r]+$/, ''))
163
+ .join('\n');
164
+ }
165
+ /**
166
+ * Rewrites the SDK's `wallets_manager::close_wallet` so it does not hold
167
+ * the wallet-manager lock while waiting on the wallet being closed. See
168
+ * the comment on `replacement` above for the deadlock this removes and
169
+ * the semantics it deliberately changes.
170
+ *
171
+ * Only the Android libraries pick this up: iOS links the prebuilt
172
+ * `libzano-plain-wallet` xcframework rather than building these sources,
173
+ * and iOS runs the same close-during-catch-up cycle without wedging.
174
+ *
175
+ * The function is located by its unique signature, delimited by brace
176
+ * counting, and then required to match the pinned original exactly
177
+ * (modulo trailing whitespace), so a pin bump that changes `close_wallet`
178
+ * in any way fails the build here instead of silently keeping (or
179
+ * dropping) a stale patch. The brace counter would be fooled by a brace
180
+ * inside a string literal, but the full-body comparison catches that case
181
+ * too: a mis-delimited body cannot match the original.
182
+ *
183
+ * @param text - The contents of the SDK's `wallets_manager.cpp`.
184
+ * @returns The patched contents. Already-patched input comes back
185
+ * unchanged, so the caller does not need to track whether it ran.
186
+ */
187
+ function patchCloseWallet(text) {
188
+ if (text.includes(patchMarker))
189
+ return text;
190
+ const anchor = 'std::string wallets_manager::close_wallet(size_t wallet_id)';
191
+ const start = text.indexOf(anchor);
192
+ if (start < 0 || text.includes(anchor, start + 1)) {
193
+ throw new Error(`Cannot find a unique wallets_manager::close_wallet to patch. ${hint}`);
194
+ }
195
+ // Take the whole function by brace balance:
196
+ let depth = 0;
197
+ let end = -1;
198
+ for (let i = text.indexOf('{', start); i >= 0 && i < text.length; ++i) {
199
+ if (text[i] === '{')
200
+ ++depth;
201
+ if (text[i] === '}' && --depth === 0) {
202
+ end = i + 1;
203
+ break;
204
+ }
205
+ }
206
+ if (end < 0) {
207
+ throw new Error(`Cannot delimit the body of wallets_manager::close_wallet. ${hint}`);
208
+ }
209
+ if (normalize(text.slice(start, end)) !== normalize(original)) {
210
+ throw new Error('wallets_manager::close_wallet does not match the pinned original ' +
211
+ `this patch was written against. ${hint}`);
212
+ }
213
+ return text.slice(0, start) + replacement + text.slice(end);
214
+ }
215
+ exports.patchCloseWallet = patchCloseWallet;
@@ -0,0 +1,37 @@
1
+ /// <reference types="node" />
2
+ export declare const tmpPath: string;
3
+ /**
4
+ * Fetches a git repo and checks out a hash.
5
+ *
6
+ * We fetch just the pinned commit rather than cloning the whole history.
7
+ * Some of these repos keep large prebuilt binaries in Git LFS, so a plain
8
+ * clone drags down every historical revision of those - slow, and prone to
9
+ * dying partway through on a flaky connection.
10
+ *
11
+ * Pass `lfsIncludes` for LFS repos to grab only the paths we build against,
12
+ * skipping the platforms we never touch.
13
+ */
14
+ export declare function getRepo(name: string, uri: string, hash: string, opts?: {
15
+ lfsIncludes?: string[];
16
+ }): Promise<void>;
17
+ /**
18
+ * Downloads & unpacks a zip file.
19
+ */
20
+ export declare function getZip(name: string, uri: string): Promise<void>;
21
+ export declare function fileExists(path: string): Promise<boolean>;
22
+ export declare function loudExec(command: string, args: string[], opts?: {
23
+ cwd?: string;
24
+ env?: NodeJS.ProcessEnv;
25
+ }): Promise<void>;
26
+ /**
27
+ * Runs a command and returns its stdout, without going through a shell.
28
+ */
29
+ export declare function captureExec(command: string, args: string[], opts?: {
30
+ cwd?: string;
31
+ }): Promise<string>;
32
+ /**
33
+ * Runs a command and returns its results.
34
+ */
35
+ export declare function quietExec(command: string, args: string[], opts?: {
36
+ cwd?: string;
37
+ }): Promise<string>;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.quietExec = exports.captureExec = exports.loudExec = exports.fileExists = exports.getZip = exports.getRepo = exports.tmpPath = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const promises_1 = require("fs/promises");
6
+ const path_1 = require("path");
7
+ exports.tmpPath = (0, path_1.join)(__dirname, '../../tmp');
8
+ /**
9
+ * Fetches a git repo and checks out a hash.
10
+ *
11
+ * We fetch just the pinned commit rather than cloning the whole history.
12
+ * Some of these repos keep large prebuilt binaries in Git LFS, so a plain
13
+ * clone drags down every historical revision of those - slow, and prone to
14
+ * dying partway through on a flaky connection.
15
+ *
16
+ * Pass `lfsIncludes` for LFS repos to grab only the paths we build against,
17
+ * skipping the platforms we never touch.
18
+ */
19
+ async function getRepo(name, uri, hash, opts = {}) {
20
+ const { lfsIncludes } = opts;
21
+ const path = (0, path_1.join)(exports.tmpPath, name);
22
+ // Set up the repo. Both steps are idempotent and run every time rather
23
+ // than behind a `fileExists(path)` check: a run killed between the mkdir
24
+ // and the init used to leave a directory that looked set up but had no
25
+ // `.git` and no remote, which every later run then skipped and no fetch
26
+ // could recover from.
27
+ if (!(await fileExists((0, path_1.join)(path, '.git'))))
28
+ console.log(`Creating ${name}...`);
29
+ await (0, promises_1.mkdir)(path, { recursive: true });
30
+ await loudExec('git', ['init', '-q'], { cwd: path });
31
+ if (!(await tryExec('git', ['remote', 'set-url', 'origin', uri], path))) {
32
+ await loudExec('git', ['remote', 'add', 'origin', uri], { cwd: path });
33
+ }
34
+ // Fetch the pinned commit, unless we already have it.
35
+ // The check also picks up hash bumps on an existing checkout:
36
+ if (!(await hasCommit(path, hash))) {
37
+ console.log(`Fetching ${name}...`);
38
+ try {
39
+ await loudExec('git', ['fetch', '--depth', '1', 'origin', hash], {
40
+ cwd: path
41
+ });
42
+ }
43
+ catch (error) {
44
+ // Not every server allows fetching a bare hash. Revoked access, a
45
+ // dropped connection and a force-pushed-away commit land here too, and
46
+ // if the full fetch succeeds without bringing the hash along, the
47
+ // checkout below fails on an unrelated-looking "unknown revision" --
48
+ // so say what actually went wrong first:
49
+ console.log(`Could not fetch ${hash} directly, retrying with a full fetch: ${String(error)}`);
50
+ // A plain `fetch` on a repo an earlier run left shallow keeps it
51
+ // shallow, which brings down the branch tips and not the pinned commit
52
+ // behind them. `--unshallow` is only valid on a shallow repo, so pick
53
+ // by whether git left its marker:
54
+ const shallow = await fileExists((0, path_1.join)(path, '.git', 'shallow'));
55
+ await loudExec('git', shallow ? ['fetch', '--unshallow', 'origin'] : ['fetch', 'origin'], { cwd: path });
56
+ }
57
+ // Fail here rather than at the checkout, which reports a missing pin as
58
+ // an opaque "unknown revision" with the fetch error already gone:
59
+ if (!(await hasCommit(path, hash))) {
60
+ throw new Error(`Could not fetch ${hash} for ${name} from ${uri}`);
61
+ }
62
+ }
63
+ // Checkout. Skipping the LFS smudge filter keeps this from blocking on an
64
+ // all-platforms LFS download; we pull the parts we need below:
65
+ console.log(`Checking out ${name}...`);
66
+ await loudExec('git', ['checkout', '-f', hash], {
67
+ cwd: path,
68
+ env: { ...process.env, GIT_LFS_SKIP_SMUDGE: '1' }
69
+ });
70
+ // Grab the LFS objects we actually build against. The empty `--exclude` is
71
+ // load-bearing: `--include` overrides `lfs.fetchinclude` but leaves any
72
+ // configured `lfs.fetchexclude` in force, so on a machine with a global
73
+ // exclude the pull reports success while leaving pointer files in place,
74
+ // and the build then links against 4KB of ASCII.
75
+ if (lfsIncludes != null) {
76
+ console.log(`Fetching ${name} LFS objects...`);
77
+ await loudExec('git', ['lfs', 'pull', `--include=${lfsIncludes.join(',')}`, '--exclude='], { cwd: path });
78
+ }
79
+ // Checkout submodules. `--force` because `update-sources` patches
80
+ // `RIPEMD160.h`, which lives inside the Zano submodule: without it the
81
+ // second run of this script aborts on "local changes would be overwritten"
82
+ // and the build is not repeatable, which `prepack` needs it to be.
83
+ await loudExec('git', ['submodule', 'update', '--init', '--recursive', '--force'], { cwd: path });
84
+ }
85
+ exports.getRepo = getRepo;
86
+ /**
87
+ * Checks whether a repo already contains a particular commit, and everything
88
+ * that commit points at.
89
+ *
90
+ * `rev-parse --verify` would accept any well-formed hash without checking
91
+ * that we have it, and `cat-file -e <hash>` proves only that the commit
92
+ * object arrived. Below `fetch.unpackLimit` git writes loose objects in no
93
+ * particular order, so a run killed mid-unpack can leave the commit present
94
+ * with its tree missing -- which this check would wave through, skipping the
95
+ * fetch that would repair it. `rev-list --objects` walks the whole thing.
96
+ */
97
+ async function hasCommit(path, hash) {
98
+ return await tryExec('git', ['rev-list', '--objects', '--quiet', hash, '--'], path);
99
+ }
100
+ /**
101
+ * Runs a command, reporting whether it succeeded rather than throwing, and
102
+ * without inheriting stdio -- for the cases where a failure is an expected
103
+ * answer instead of an error.
104
+ */
105
+ async function tryExec(command, args, cwd) {
106
+ return await new Promise(resolve => {
107
+ const child = (0, child_process_1.spawn)(command, args, { cwd, stdio: 'ignore' });
108
+ child.on('error', () => resolve(false));
109
+ child.on('exit', code => resolve(code === 0));
110
+ });
111
+ }
112
+ /**
113
+ * Downloads & unpacks a zip file.
114
+ */
115
+ async function getZip(name, uri) {
116
+ const path = (0, path_1.join)(exports.tmpPath, name);
117
+ if (!(await fileExists(path))) {
118
+ console.log(`Getting ${name}...`);
119
+ await loudExec('curl', ['-L', '-o', path, uri]);
120
+ }
121
+ // Unzip:
122
+ await loudExec('unzip', ['-u', path]);
123
+ }
124
+ exports.getZip = getZip;
125
+ async function fileExists(path) {
126
+ return await (0, promises_1.access)(path).then(() => true, () => false);
127
+ }
128
+ exports.fileExists = fileExists;
129
+ async function loudExec(command, args, opts = {}) {
130
+ const { cwd = exports.tmpPath, env = process.env } = opts;
131
+ return await new Promise((resolve, reject) => {
132
+ const child = (0, child_process_1.spawn)(command, args, {
133
+ cwd,
134
+ stdio: 'inherit',
135
+ env
136
+ });
137
+ child.on('error', reject);
138
+ child.on('exit', code => {
139
+ if (code === 0) {
140
+ resolve();
141
+ }
142
+ else {
143
+ reject(new Error(`${command} exited with code ${String(code)}`));
144
+ }
145
+ });
146
+ });
147
+ }
148
+ exports.loudExec = loudExec;
149
+ /**
150
+ * Runs a command and returns its stdout, without going through a shell.
151
+ */
152
+ async function captureExec(command, args, opts = {}) {
153
+ const { cwd = exports.tmpPath } = opts;
154
+ return await new Promise((resolve, reject) => {
155
+ const child = (0, child_process_1.spawn)(command, args, {
156
+ cwd,
157
+ stdio: ['ignore', 'pipe', 'inherit']
158
+ });
159
+ let out = '';
160
+ child.stdout.on('data', chunk => {
161
+ out += String(chunk);
162
+ });
163
+ child.on('error', reject);
164
+ // `close` rather than `exit`: the process can exit while stdout still has
165
+ // buffered data, which would truncate the output we are here to collect.
166
+ // `close` waits for the stdio streams too.
167
+ child.on('close', code => {
168
+ if (code === 0)
169
+ resolve(out.replace(/\n$/, ''));
170
+ else
171
+ reject(new Error(`${command} exited with code ${String(code)}`));
172
+ });
173
+ });
174
+ }
175
+ exports.captureExec = captureExec;
176
+ /**
177
+ * Runs a command and returns its results.
178
+ */
179
+ async function quietExec(command, args, opts = {}) {
180
+ const { cwd = exports.tmpPath } = opts;
181
+ return (0, child_process_1.execSync)(command + ' ' + args.join(' '), {
182
+ cwd,
183
+ encoding: 'utf8'
184
+ }).replace(/\n$/, '');
185
+ }
186
+ exports.quietExec = quietExec;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Finds llvm-objcopy in Homebrew.
3
+ *
4
+ * Homebrew doesn't symlink LLVM into the normal location,
5
+ * to avoid conflicting with Xcode's built-in tools.
6
+ * We can get around this by looking in the right places.
7
+ */
8
+ export declare function getObjcopyPath(): Promise<string>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getObjcopyPath = void 0;
4
+ const common_1 = require("./common");
5
+ /**
6
+ * Finds llvm-objcopy in Homebrew.
7
+ *
8
+ * Homebrew doesn't symlink LLVM into the normal location,
9
+ * to avoid conflicting with Xcode's built-in tools.
10
+ * We can get around this by looking in the right places.
11
+ */
12
+ async function getObjcopyPath() {
13
+ const whichPath = await (0, common_1.quietExec)('which', ['llvm-objcopy']).catch(() => { });
14
+ if (whichPath != null)
15
+ return whichPath;
16
+ const paths = [
17
+ '/opt/homebrew/opt/llvm/bin/llvm-objcopy',
18
+ '/usr/local/opt/llvm/bin/llvm-objcopy' // for Intel Macs
19
+ ];
20
+ for (const path of paths) {
21
+ if (await (0, common_1.fileExists)(path))
22
+ return path;
23
+ }
24
+ throw new Error('Please install `llvm-objcopy` using `brew install llvm`.');
25
+ }
26
+ exports.getObjcopyPath = getObjcopyPath;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Directories the Zano SDK creates under the working directory the app hands
3
+ * it, and that `ios/ZanoModule.mm` therefore has to pre-create so it can
4
+ * exclude them from device backups -- the wallet files hold the seed and
5
+ * spend keys.
6
+ *
7
+ * Keep this in sync with the `prepareZanoDirectory` calls in
8
+ * `ios/ZanoModule.mm`. `update-sources.ts` fails the build if the SDK
9
+ * declares a directory that is missing here.
10
+ */
11
+ export declare const sdkFolders: string[];
12
+ /**
13
+ * Extracts the directory names the SDK declares, sorted.
14
+ *
15
+ * Matches `#define`s whose name mentions a folder, so `APP_CONFIG_FOLDER`
16
+ * and `WALLETS_FOLDER_NAME` count but `APP_CONFIG_FILENAME` does not.
17
+ *
18
+ * This catches directories declared as `#define`d names, which is how the
19
+ * SDK has always declared them. It cannot catch a path composed inline, so
20
+ * it is a tripwire rather than a proof.
21
+ *
22
+ * @param apiText - The contents of the SDK's `plain_wallet_api.cpp`.
23
+ * @throws If nothing matches at all, which means the SDK changed how it
24
+ * declares directories and this check needs rewriting rather than silently
25
+ * reporting "no new folders".
26
+ */
27
+ export declare function findSdkFolders(apiText: string): string[];
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findSdkFolders = exports.sdkFolders = void 0;
4
+ /**
5
+ * Directories the Zano SDK creates under the working directory the app hands
6
+ * it, and that `ios/ZanoModule.mm` therefore has to pre-create so it can
7
+ * exclude them from device backups -- the wallet files hold the seed and
8
+ * spend keys.
9
+ *
10
+ * Keep this in sync with the `prepareZanoDirectory` calls in
11
+ * `ios/ZanoModule.mm`. `update-sources.ts` fails the build if the SDK
12
+ * declares a directory that is missing here.
13
+ */
14
+ exports.sdkFolders = ['app_config', 'logs', 'wallets'];
15
+ /**
16
+ * Extracts the directory names the SDK declares, sorted.
17
+ *
18
+ * Matches `#define`s whose name mentions a folder, so `APP_CONFIG_FOLDER`
19
+ * and `WALLETS_FOLDER_NAME` count but `APP_CONFIG_FILENAME` does not.
20
+ *
21
+ * This catches directories declared as `#define`d names, which is how the
22
+ * SDK has always declared them. It cannot catch a path composed inline, so
23
+ * it is a tripwire rather than a proof.
24
+ *
25
+ * @param apiText - The contents of the SDK's `plain_wallet_api.cpp`.
26
+ * @throws If nothing matches at all, which means the SDK changed how it
27
+ * declares directories and this check needs rewriting rather than silently
28
+ * reporting "no new folders".
29
+ */
30
+ function findSdkFolders(apiText) {
31
+ const found = new Set();
32
+ const regexp = /^#define\s+\w*(?:FOLDER|_DIR)\w*\s+"([^"]*)"/gm;
33
+ let match;
34
+ while ((match = regexp.exec(apiText)) != null) {
35
+ found.add(match[1]);
36
+ }
37
+ if (found.size === 0) {
38
+ throw new Error('Found no folder definitions in plain_wallet_api.cpp. The SDK has ' +
39
+ 'probably changed how it declares them, so this check needs updating.');
40
+ }
41
+ return [...found].sort((a, b) => a.localeCompare(b));
42
+ }
43
+ exports.findSdkFolders = findSdkFolders;