@qntx/wallet-native 0.0.0-stage → 0.4.0

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 (69) hide show
  1. package/README.md +123 -2
  2. package/WalletNative.podspec +74 -0
  3. package/android/CMakeLists.txt +99 -0
  4. package/android/build.gradle +177 -0
  5. package/android/consumer-rules.pro +4 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +29 -0
  8. package/android/src/main/java/com/margelo/nitro/qntxwallet/AppLifecycle.kt +35 -0
  9. package/android/src/main/java/com/margelo/nitro/qntxwallet/KernelPbkdf2.java +59 -0
  10. package/android/src/main/java/com/margelo/nitro/qntxwallet/Keystore.kt +280 -0
  11. package/android/src/main/java/com/margelo/nitro/qntxwallet/Passkey.kt +147 -0
  12. package/android/src/main/java/com/margelo/nitro/qntxwallet/VaultDirectory.kt +28 -0
  13. package/android/src/main/java/com/margelo/nitro/qntxwallet/WalletNativePackage.java +30 -0
  14. package/cpp/host/HybridKernelHost.cpp +162 -0
  15. package/cpp/host/HybridKernelHost.hpp +49 -0
  16. package/cpp/host/KernelFile.cpp +120 -0
  17. package/cpp/host/KernelFile.hpp +24 -0
  18. package/cpp/host/Keystore.hpp +70 -0
  19. package/cpp/host/Lifecycle.cpp +77 -0
  20. package/cpp/host/Lifecycle.hpp +71 -0
  21. package/cpp/host/Passkey.hpp +39 -0
  22. package/cpp/host/VaultDirectory.hpp +16 -0
  23. package/cpp/platform/KeystoreAndroid.cpp +175 -0
  24. package/cpp/platform/KeystoreIos.mm +300 -0
  25. package/cpp/platform/LifecycleAndroid.cpp +55 -0
  26. package/cpp/platform/LifecycleIos.mm +36 -0
  27. package/cpp/platform/PasskeyAndroid.cpp +118 -0
  28. package/cpp/platform/PasskeyIos.mm +20 -0
  29. package/cpp/platform/VaultDirectory.mm +58 -0
  30. package/cpp/platform/VaultDirectoryAndroid.cpp +35 -0
  31. package/cpp/runtime/KernelBackends.cpp +297 -0
  32. package/cpp/runtime/KernelBackends.hpp +20 -0
  33. package/cpp/runtime/KernelBuffer.cpp +12 -0
  34. package/cpp/runtime/KernelBuffer.hpp +96 -0
  35. package/cpp/runtime/KernelGlobals.cpp +535 -0
  36. package/cpp/runtime/KernelGlobals.hpp +107 -0
  37. package/cpp/runtime/KernelJs.cpp +155 -0
  38. package/cpp/runtime/KernelJs.hpp +81 -0
  39. package/cpp/runtime/KernelKeystore.cpp +224 -0
  40. package/cpp/runtime/KernelKeystore.hpp +28 -0
  41. package/cpp/runtime/KernelPasskey.cpp +137 -0
  42. package/cpp/runtime/KernelPasskey.hpp +23 -0
  43. package/cpp/runtime/KernelRuntime.cpp +496 -0
  44. package/cpp/runtime/KernelRuntime.hpp +94 -0
  45. package/cpp/runtime/platform/KernelPbkdf2.cpp +198 -0
  46. package/cpp/runtime/platform/KernelPbkdf2.hpp +38 -0
  47. package/dist/index.d.mts +44 -0
  48. package/dist/index.mjs +170 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/kernel/generated/kernel-entry.js +42967 -0
  51. package/kernel/generated/kernel.manifest.json +80 -0
  52. package/nitrogen/generated/.gitattributes +1 -0
  53. package/nitrogen/generated/android/WalletNative+autolinking.cmake +81 -0
  54. package/nitrogen/generated/android/WalletNative+autolinking.gradle +27 -0
  55. package/nitrogen/generated/android/WalletNativeOnLoad.cpp +49 -0
  56. package/nitrogen/generated/android/WalletNativeOnLoad.hpp +34 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/qntxwallet/WalletNativeOnLoad.kt +35 -0
  58. package/nitrogen/generated/ios/WalletNative+autolinking.rb +62 -0
  59. package/nitrogen/generated/ios/WalletNative-Swift-Cxx-Bridge.cpp +17 -0
  60. package/nitrogen/generated/ios/WalletNative-Swift-Cxx-Bridge.hpp +27 -0
  61. package/nitrogen/generated/ios/WalletNative-Swift-Cxx-Umbrella.hpp +38 -0
  62. package/nitrogen/generated/ios/WalletNativeAutolinking.mm +35 -0
  63. package/nitrogen/generated/ios/WalletNativeAutolinking.swift +16 -0
  64. package/nitrogen/generated/shared/c++/HybridKernelHostSpec.cpp +25 -0
  65. package/nitrogen/generated/shared/c++/HybridKernelHostSpec.hpp +69 -0
  66. package/nitrogen/generated/shared/c++/KernelExitReason.hpp +84 -0
  67. package/package.json +61 -4
  68. package/react-native.config.js +14 -0
  69. package/scripts/compile-kernel.mjs +118 -0
@@ -0,0 +1,84 @@
1
+ ///
2
+ /// KernelExitReason.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/NitroHash.hpp>)
11
+ #include <NitroModules/NitroHash.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
16
+ #include <NitroModules/JSIConverter.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
21
+ #include <NitroModules/NitroDefines.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+
26
+ namespace margelo::nitro::qntxwallet {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (KernelExitReason).
30
+ */
31
+ enum class KernelExitReason {
32
+ LOCKED SWIFT_NAME(locked) = 0,
33
+ CRASHED SWIFT_NAME(crashed) = 1,
34
+ INTEGRITY SWIFT_NAME(integrity) = 2,
35
+ BACKGROUND SWIFT_NAME(background) = 3,
36
+ } CLOSED_ENUM;
37
+
38
+ } // namespace margelo::nitro::qntxwallet
39
+
40
+ namespace margelo::nitro {
41
+
42
+ // C++ KernelExitReason <> JS KernelExitReason (union)
43
+ template <>
44
+ struct JSIConverter<margelo::nitro::qntxwallet::KernelExitReason> final {
45
+ static inline margelo::nitro::qntxwallet::KernelExitReason fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
47
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
48
+ case hashString("locked"): return margelo::nitro::qntxwallet::KernelExitReason::LOCKED;
49
+ case hashString("crashed"): return margelo::nitro::qntxwallet::KernelExitReason::CRASHED;
50
+ case hashString("integrity"): return margelo::nitro::qntxwallet::KernelExitReason::INTEGRITY;
51
+ case hashString("background"): return margelo::nitro::qntxwallet::KernelExitReason::BACKGROUND;
52
+ default: [[unlikely]]
53
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum KernelExitReason - invalid value!");
54
+ }
55
+ }
56
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::qntxwallet::KernelExitReason arg) {
57
+ switch (arg) {
58
+ case margelo::nitro::qntxwallet::KernelExitReason::LOCKED: return JSIConverter<std::string>::toJSI(runtime, "locked");
59
+ case margelo::nitro::qntxwallet::KernelExitReason::CRASHED: return JSIConverter<std::string>::toJSI(runtime, "crashed");
60
+ case margelo::nitro::qntxwallet::KernelExitReason::INTEGRITY: return JSIConverter<std::string>::toJSI(runtime, "integrity");
61
+ case margelo::nitro::qntxwallet::KernelExitReason::BACKGROUND: return JSIConverter<std::string>::toJSI(runtime, "background");
62
+ default: [[unlikely]]
63
+ throw std::invalid_argument("Cannot convert KernelExitReason to JS - invalid value: "
64
+ + std::to_string(static_cast<int>(arg)) + "!");
65
+ }
66
+ }
67
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68
+ if (!value.isString()) {
69
+ return false;
70
+ }
71
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
72
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
73
+ case hashString("locked"):
74
+ case hashString("crashed"):
75
+ case hashString("integrity"):
76
+ case hashString("background"):
77
+ return true;
78
+ default:
79
+ return false;
80
+ }
81
+ }
82
+ };
83
+
84
+ } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@qntx/wallet-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.4.0",
4
+ "description": "Native kernel runtime host for the qntx wallet — CBOR transport, KernelHost adapter and createNativeWallet",
5
+ "license": "MIT OR Apache-2.0",
6
+ "author": "QuantX <x@qntx.org>",
7
+ "files": [
8
+ "dist",
9
+ "cpp",
10
+ "android",
11
+ "kernel/generated",
12
+ "nitrogen",
13
+ "scripts",
14
+ "react-native.config.js",
15
+ "WalletNative.podspec",
16
+ "!android/build",
17
+ "!android/.cxx",
18
+ "!**/.gradle",
19
+ "!tests"
20
+ ],
21
+ "type": "module",
22
+ "main": "dist/index.mjs",
23
+ "types": "dist/index.d.mts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.mts",
27
+ "import": "./dist/index.mjs",
28
+ "default": "./dist/index.mjs"
29
+ }
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "build": "vp pack",
36
+ "test": "vp test",
37
+ "typecheck": "tsc --noEmit -p tsconfig.json",
38
+ "codegen": "nitrogen",
39
+ "check:pkg": "vp pack && bun ../../scripts/check-dist-deps.ts . && publint && attw --pack . --profile esm-only && bun ../../scripts/check-native-pack.ts ."
40
+ },
41
+ "dependencies": {
42
+ "cborg": "6.1.4"
43
+ },
44
+ "devDependencies": {
45
+ "@arethetypeswrong/cli": "0.18.5",
46
+ "@qntx/nostr": "0.15.3",
47
+ "@qntx/nostr-native": "0.15.3",
48
+ "@qntx/spark": "0.5.2",
49
+ "@qntx/spark-native": "0.5.2",
50
+ "@qntx/tsconfig": "1.0.1",
51
+ "@scure/base": "2.4.0",
52
+ "nitrogen": "0.37.1",
53
+ "publint": "0.3.25",
54
+ "react-native-nitro-modules": "0.37.1"
55
+ },
56
+ "peerDependencies": {
57
+ "@qntx/nostr-native": "0.15.3",
58
+ "@qntx/spark-native": "0.5.2",
59
+ "@qntx/wallet": "0.4.0",
60
+ "react-native": "~0.88.0",
61
+ "react-native-nitro-modules": "~0.37.1"
62
+ }
63
+ }
@@ -0,0 +1,14 @@
1
+ // oxlint-disable import/no-commonjs, unicorn/prefer-module -- the RN CLI loads
2
+ // react-native.config.js via require; CJS is the required format.
3
+
4
+ // https://github.com/react-native-community/cli/blob/main/docs/dependencies.md
5
+ module.exports = {
6
+ dependency: {
7
+ platforms: {
8
+ /** @type {import("@react-native-community/cli-types").IOSDependencyParams} */
9
+ ios: {},
10
+ /** @type {import("@react-native-community/cli-types").AndroidDependencyParams} */
11
+ android: {},
12
+ },
13
+ },
14
+ };
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Compile the shipped kernel bundle to Hermes bytecode with the consuming app's own hermesc,
4
+ * resolved through its installed react-native → hermes-compiler. Hermes only loads its own bytecode
5
+ * version, so the bytecode is compiled at the app's native build time, never shipped.
6
+ *
7
+ * Usage: node compile-kernel.mjs <outDir> Emits <outDir>/kernel.hpp (KERNEL_BYTECODE /
8
+ * KERNEL_BYTECODE_SIZE), rewritten only when the content changes so incremental builds do not
9
+ * recompile C++.
10
+ */
11
+ import { execFileSync } from "node:child_process";
12
+ import { createHash } from "node:crypto";
13
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
14
+ import { createRequire } from "node:module";
15
+ import { dirname, resolve } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const { 2: outDirArg, 3: reactNativeDir } = process.argv;
19
+ if (
20
+ outDirArg === undefined ||
21
+ outDirArg === "" ||
22
+ reactNativeDir === undefined ||
23
+ reactNativeDir === ""
24
+ ) {
25
+ console.error("usage: node compile-kernel.mjs <outDir> <reactNativeDir>");
26
+ process.exit(2);
27
+ }
28
+ const outDir = resolve(outDirArg);
29
+ const packageDir = fileURLToPath(new URL("..", import.meta.url));
30
+ const generated = resolve(packageDir, "kernel", "generated");
31
+ const entry = resolve(generated, "kernel-entry.js");
32
+ const manifestPath = resolve(generated, "kernel.manifest.json");
33
+
34
+ // The manifest's bundle SHA is the contract: a tampered or stale bundle must
35
+ // fail the native build instead of shipping a kernel nobody recorded.
36
+ /**
37
+ * @param {unknown} value
38
+ * @returns {value is { bundles?: { file: string; sha256: string }[] }}
39
+ */
40
+ function isManifest(value) {
41
+ return typeof value === "object" && value !== null && "bundles" in value;
42
+ }
43
+ const parsed = /** @type {unknown} */ (JSON.parse(readFileSync(manifestPath, "utf8")));
44
+ if (!isManifest(parsed)) {
45
+ console.error("kernel.manifest.json: expected an object with a bundles field");
46
+ process.exit(1);
47
+ }
48
+ const declared = parsed.bundles?.find((b) => b.file === "kernel-entry.js")?.sha256;
49
+ const actual = createHash("sha256").update(readFileSync(entry)).digest("hex");
50
+ if (typeof declared !== "string" || declared !== actual) {
51
+ console.error(
52
+ `kernel-entry.js sha256 mismatch: kernel.manifest.json declares ` +
53
+ `${declared ?? "<missing>"}, the bundle hashes ${actual} — ` +
54
+ `run bun run native:kernel and commit both artifacts`,
55
+ );
56
+ process.exit(1);
57
+ }
58
+
59
+ // Resolve through the app's own react-native so the hermesc version matches the
60
+ // Hermes runtime the app links — the entire reason bytecode is compiled here.
61
+ // The app's react-native root is passed explicitly by the build system — a
62
+ // package has no reliable way to resolve it from its own tree (the package may
63
+ // be built from its source dir via :path or a file: install).
64
+ const rnPkg = resolve(reactNativeDir, "package.json");
65
+ const requireFromRn = createRequire(rnPkg);
66
+ const hermesPkg = requireFromRn.resolve("hermes-compiler/package.json");
67
+ const platformDir =
68
+ process.platform === "darwin"
69
+ ? "osx-bin"
70
+ : process.platform === "linux"
71
+ ? "linux64-bin"
72
+ : "win64-bin";
73
+ const hermesc = resolve(
74
+ dirname(hermesPkg),
75
+ "hermesc",
76
+ platformDir,
77
+ process.platform === "win32" ? "hermesc.exe" : "hermesc",
78
+ );
79
+
80
+ mkdirSync(outDir, { recursive: true });
81
+ const tmpHbc = resolve(outDir, ".kernel.hbc");
82
+ try {
83
+ execFileSync(hermesc, ["-O", "-emit-async-break-check", "-emit-binary", "-out", tmpHbc, entry], {
84
+ stdio: ["ignore", "pipe", "inherit"],
85
+ });
86
+ } catch (error) {
87
+ console.error(
88
+ `hermesc failed on ${entry}: ${error instanceof Error ? error.message : String(error)}`,
89
+ );
90
+ process.exit(1);
91
+ }
92
+ const bytes = readFileSync(tmpHbc);
93
+ rmSync(tmpHbc);
94
+
95
+ const body = [
96
+ "// Generated by scripts/compile-kernel.mjs — do not edit.",
97
+ "#pragma once",
98
+ "",
99
+ "#include <cstdint>",
100
+ "#include <cstddef>",
101
+ "",
102
+ "namespace qntx {",
103
+ "inline constexpr uint8_t KERNEL_BYTECODE[] = {",
104
+ ];
105
+ for (let i = 0; i < bytes.length; i += 16) {
106
+ const chunk = [...bytes.subarray(i, i + 16)]
107
+ .map((b) => `0x${b.toString(16).padStart(2, "0")}`)
108
+ .join(",");
109
+ body.push(` ${chunk}${i + 16 < bytes.length ? "," : ""}`);
110
+ }
111
+ body.push("};", `inline constexpr size_t KERNEL_BYTECODE_SIZE = ${bytes.length};`, "}");
112
+
113
+ const header = `${body.join("\n")}\n`;
114
+ const headerPath = resolve(outDir, "kernel.hpp");
115
+ if (!existsSync(headerPath) || readFileSync(headerPath, "utf8") !== header) {
116
+ writeFileSync(headerPath, header);
117
+ }
118
+ console.log(`kernel.hpp: ${bytes.length} bytes compiled`);