summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsneurlax <sneurlax@gmail.com>2024-10-16 17:14:41 -0500
committerGitHub <noreply@github.com>2024-10-16 17:14:41 -0500
commit6260fed0d483dad6d8af016e1b223c36c13dca5b (patch)
treeff6accbbe075eef647b3666fce42ab8cf2874778
parentb556f50129d1a55c81cb5bcdf749f154137c9999 (diff)
parent939040032f6e22529ccb6b5f54d9c48fc94db3d6 (diff)
Merge branch 'master' into rust
-rw-r--r--.github/workflows/full_check.yaml30
-rw-r--r--.gitignore3
-rw-r--r--impls/monero.ts/README.md17
-rw-r--r--impls/monero.ts/checksum.ts12
-rw-r--r--impls/monero.ts/src/bindings.ts567
-rw-r--r--impls/monero.ts/src/pending_transaction.ts28
-rw-r--r--impls/monero.ts/src/symbols.ts553
-rw-r--r--impls/monero.ts/src/transaction_history.ts16
-rw-r--r--impls/monero.ts/src/transaction_info.ts40
-rw-r--r--impls/monero.ts/src/utils.ts14
-rw-r--r--impls/monero.ts/src/wallet.ts68
-rw-r--r--impls/monero.ts/src/wallet_manager.ts5
-rw-r--r--monero_libwallet2_api_c/CMakeLists.txt1
-rw-r--r--monero_libwallet2_api_c/src/main/cpp/helpers.cpp67
-rw-r--r--patches/monero/0019-fix-for-coin-control-patch.patch97
-rwxr-xr-xtests/compare.ts80
-rwxr-xr-xtests/deno.jsonc5
-rwxr-xr-xtests/deno.lock90
-rwxr-xr-xtests/regression.test.ts39
-rwxr-xr-xtests/utils.ts131
20 files changed, 1235 insertions, 628 deletions
diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml
index add89b7..eaec860 100644
--- a/.github/workflows/full_check.yaml
+++ b/.github/workflows/full_check.yaml
@@ -391,7 +391,7 @@ jobs:
key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }}
- name: build
run: |
- ./build_single.sh ${{ matrix.coin }} aarch64-host-apple-darwin -j$(sysctl -n hw.logicalcpu)
+ ./build_single.sh ${{ matrix.coin }} aarch64-host-apple-darwin -j$(sysctl -n hw.logicalcpu)
- name: rename artifacts
run: |
mkdir release/gh/
@@ -448,7 +448,7 @@ jobs:
key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }}
- name: build
run: |
- ./build_single.sh ${{ matrix.coin }} host-apple-ios -j$(sysctl -n hw.logicalcpu)
+ ./build_single.sh ${{ matrix.coin }} host-apple-ios -j$(sysctl -n hw.logicalcpu)
- name: rename artifacts
run: |
mkdir release/gh/
@@ -586,6 +586,32 @@ jobs:
cd impls/monero.ts
deno run --unstable-ffi --allow-ffi checksum.ts
+ regression_check:
+ strategy:
+ matrix:
+ coin: [monero, wownero]
+ needs: [
+ lib_linux
+ ]
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: denoland/setup-deno@v2
+ with:
+ deno-version: v2.x
+
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: linux ${{ matrix.coin }}
+ path: release/${{ matrix.coin }}
+
+ - name: Run regression tests
+ run: COIN="${{ matrix.coin }}" deno test -A tests/
+
comment_pr:
name: comment on pr
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index 1070540..2dbc30e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
release/
build/
+tests/monero-cli
+tests/libs
+tests/wallets
diff --git a/impls/monero.ts/README.md b/impls/monero.ts/README.md
index e3b20f6..fe06467 100644
--- a/impls/monero.ts/README.md
+++ b/impls/monero.ts/README.md
@@ -7,13 +7,20 @@
This library does not ship with `monero_c` libraries.\
To use these bindings you have to bring your own `monero_c` libraries.\
There are at least two ways to do so:
+
- Ahead-of-time, during builds where you only ship necessary library for a given platform.\
- See [monero-tui](https://github.com/Im-Beast/monero-tui/blob/main/.github/workflows/dev-build.yml) build workflow as an example of doing so.
+ See [monero-tui](https://github.com/Im-Beast/monero-tui/blob/main/.github/workflows/dev-build.yml) build workflow as
+ an example of doing so.
```ts
- import { loadDylib, Wallet, WalletManager } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
+ import {
+ loadMoneroDylib,
+ Wallet,
+ WalletManager,
+ } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
// Try to load dylib from the default lib/* path
- loadDylib();
+ // You can also use loadWowneroDylib for Wownero
+ loadMoneroDylib();
const wm = await WalletManager.new();
const wallet = await Wallet.create(wm, "./my_wallet", "password");
@@ -27,11 +34,11 @@ There are at least two ways to do so:
```ts
import { dlopen } from "jsr:@denosaurs/plug";
// It's recommened to put the monero.ts github link into your import_map to reduce the url clutter
- import { loadDylib, symbols, Wallet, WalletManager } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
+ import { loadMoneroDylib, symbols, Wallet, WalletManager } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
// Load dylib loaded by plug
const lib = await dlopen(..., symbols);
- loadDylib(lib);
+ loadMoneroDylib(lib);
const wm = await WalletManager.new();
const wallet = await Wallet.create(wm, "./my_wallet", "password");
diff --git a/impls/monero.ts/checksum.ts b/impls/monero.ts/checksum.ts
index 22d3038..b0d12b0 100644
--- a/impls/monero.ts/checksum.ts
+++ b/impls/monero.ts/checksum.ts
@@ -1,8 +1,8 @@
import { moneroChecksum } from "./checksum_monero.ts";
-import { readCString } from "./src/utils.ts";
-import { dylib, loadDylib } from "./src/bindings.ts";
+import { getSymbol, readCString } from "./src/utils.ts";
+import { dylib, loadMoneroDylib } from "./src/bindings.ts";
-loadDylib();
+loadMoneroDylib();
export class ChecksumError extends Error {
readonly code: number;
@@ -21,7 +21,7 @@ export class ChecksumError extends Error {
* @returns {ChecksumError} which contains information about why checksum failed
*/
export async function validateChecksum(): Promise<ChecksumError | null> {
- const cppHeaderHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_h(), false);
+ const cppHeaderHash = await readCString(await getSymbol("checksum_wallet2_api_c_h")!(), false);
const tsHeaderHash = moneroChecksum.wallet2_api_c_h_sha256;
const errors: string[] = [];
@@ -32,14 +32,14 @@ export async function validateChecksum(): Promise<ChecksumError | null> {
errorCode++;
}
- const cppSourceHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_cpp(), false);
+ const cppSourceHash = await readCString(await getSymbol("checksum_wallet2_api_c_cpp")!(), false);
const tsSourceHash = moneroChecksum.wallet2_api_c_cpp_sha256;
if (cppSourceHash !== tsSourceHash) {
errors.push(`ERR: CPP source file check mismatch ${cppSourceHash} == ${tsSourceHash}`);
errorCode++;
}
- const cppExportHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_exp(), false);
+ const cppExportHash = await readCString(await getSymbol("checksum_wallet2_api_c_exp")!(), false);
const tsExportHash = moneroChecksum.wallet2_api_c_exp_sha256;
if (cppExportHash !== tsExportHash) {
if (Deno.build.os !== "darwin") {
diff --git a/impls/monero.ts/src/bindings.ts b/impls/monero.ts/src/bindings.ts
index b854f7d..93ce8f9 100644
--- a/impls/monero.ts/src/bindings.ts
+++ b/impls/monero.ts/src/bindings.ts
@@ -1,542 +1,8 @@
-export const symbols = {
- "MONERO_WalletManagerFactory_getWalletManager": {
- nonblocking: true,
- parameters: [],
- // void*
- result: "pointer",
- },
+import { type Dylib, moneroSymbols, type MoneroTsDylib, wowneroSymbols, type WowneroTsDylib } from "./symbols.ts";
- //#region WalletManager
- "MONERO_WalletManager_createWallet": {
- nonblocking: true,
- // void* wm_ptr, const char* path, const char* password, const char* language, int networkType
- parameters: ["pointer", "pointer", "pointer", "pointer", "i32"],
- // void*
- result: "pointer",
- },
- "MONERO_WalletManager_openWallet": {
- nonblocking: true,
- // void* wm_ptr, const char* path, const char* password, int networkType
- "parameters": ["pointer", "pointer", "pointer", "i32"],
- // void*
- result: "pointer",
- },
- "MONERO_WalletManager_recoveryWallet": {
- nonblocking: true,
- // void* wm_ptr, const char* path, const char* password, const char* mnemonic,
- // int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset
- parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"],
- // void*
- result: "pointer",
- },
- "MONERO_WalletManager_blockchainHeight": {
- nonblocking: true,
- // void* wm_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_WalletManager_blockchainTargetHeight": {
- nonblocking: true,
- // void* wm_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_WalletManager_setDaemonAddress": {
- nonblocking: true,
- // void* wm_ptr, const char* address
- parameters: ["pointer", "pointer"],
- // void
- result: "void",
- },
- //#endregion
+export let dylib: Dylib;
- //#region Wallet
- "MONERO_Wallet_init": {
- nonblocking: true,
- // void* wallet_ptr, const char* daemon_address, uint64_t upper_transaction_size_limit,
- // const char* daemon_username, const char* daemon_password, bool use_ssl, bool lightWallet,
- // const char* proxy_address
- parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"],
- // bool
- result: "bool",
- },
- "MONERO_Wallet_init3": {
- nonblocking: true,
- // void* wallet_ptr, const char* argv0, const char* default_log_base_name,
- // const char* log_path, bool console
- parameters: ["pointer", "pointer", "pointer", "pointer", "bool"],
- // void
- result: "void",
- },
- "MONERO_Wallet_setTrustedDaemon": {
- nonblocking: true,
- // void* wallet_ptr, bool arg
- parameters: ["pointer", "bool"],
- // void
- result: "void",
- },
- "MONERO_Wallet_startRefresh": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // void
- result: "void",
- },
- "MONERO_Wallet_refreshAsync": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // void
- result: "void",
- },
- "MONERO_Wallet_blockChainHeight": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_Wallet_daemonBlockChainHeight": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_Wallet_synchronized": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // bool
- result: "bool",
- },
- "MONERO_Wallet_store": {
- nonblocking: true,
- // void* wallet_ptr, const char* path
- parameters: ["pointer", "pointer"],
- // bool
- result: "bool",
- },
- "MONERO_Wallet_address": {
- nonblocking: true,
- // void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex
- parameters: ["pointer", "u64", "u64"],
- // char*
- result: "pointer",
- },
- "MONERO_Wallet_balance": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex
- parameters: ["pointer", "u32"],
- // uint64_t
- result: "u64",
- },
- "MONERO_Wallet_unlockedBalance": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex
- parameters: ["pointer", "u32"],
- // uint64_t
- result: "u64",
- },
- "MONERO_Wallet_addSubaddressAccount": {
- nonblocking: true,
- // void* wallet_ptr, const char* label
- parameters: ["pointer", "pointer"],
- // void
- result: "void",
- },
- "MONERO_Wallet_numSubaddressAccounts": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // size_t
- result: "usize",
- },
- "MONERO_Wallet_addSubaddress": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex, const char* label
- parameters: ["pointer", "u32", "pointer"],
- // void
- result: "void",
- },
- "MONERO_Wallet_numSubaddresses": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex
- parameters: ["pointer", "u32"],
- // size_t
- result: "usize",
- },
- "MONERO_Wallet_getSubaddressLabel": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex
- parameters: ["pointer", "u32", "u32"],
- // const char*
- result: "pointer",
- },
- "MONERO_Wallet_setSubaddressLabel": {
- nonblocking: true,
- // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label
- parameters: ["pointer", "u32", "u32", "pointer"],
- // void
- result: "void",
- },
- "MONERO_Wallet_status": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // int
- result: "i32",
- },
- "MONERO_Wallet_errorString": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // char*
- result: "pointer",
- },
- "MONERO_Wallet_history": {
- nonblocking: true,
- // void* wallet_ptr
- parameters: ["pointer"],
- // void*
- result: "pointer",
- },
- "MONERO_Wallet_createTransaction": {
- nonblocking: true,
- // void* wallet_ptr, const char* dst_addr, const char* payment_id
- // uint64_t amount, uint32_t mixin_count, int pendingTransactionPriority,
- // uint32_t subaddr_account, const char* preferredInputs, const char* separator
- parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"],
- // void*
- result: "pointer",
- },
- "MONERO_Wallet_amountFromString": {
- nonblocking: true,
- // const char* amount
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- //#endregion
-
- //#region TransactionHistory
- "MONERO_TransactionHistory_count": {
- nonblocking: true,
- // void* txHistory_ptr
- parameters: ["pointer"],
- // int
- result: "i32",
- },
- "MONERO_TransactionHistory_transaction": {
- nonblocking: true,
- // void* txHistory_ptr, int index
- parameters: ["pointer", "i32"],
- // void*
- result: "pointer",
- },
- "MONERO_TransactionHistory_transactionById": {
- nonblocking: true,
- // void* txHistory_ptr, const char* id
- parameters: ["pointer", "pointer"],
- // void*
- result: "pointer",
- },
- "MONERO_TransactionHistory_refresh": {
- nonblocking: true,
- // void* txHistory_ptr
- parameters: ["pointer"],
- // void
- result: "void",
- },
- "MONERO_TransactionHistory_setTxNote": {
- nonblocking: true,
- // void* txHistory_ptr, const char* txid, const char* note
- parameters: ["pointer", "pointer", "pointer"],
- // void
- result: "void",
- },
- //#endregion
-
- //#region TransactionInfo
- "MONERO_TransactionInfo_direction": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // int
- result: "i32",
- },
- "MONERO_TransactionInfo_isPending": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // bool
- result: "bool",
- },
- "MONERO_TransactionInfo_isFailed": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // bool
- result: "bool",
- },
- "MONERO_TransactionInfo_isCoinbase": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // bool
- result: "bool",
- },
- "MONERO_TransactionInfo_amount": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_fee": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_blockHeight": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_description": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_TransactionInfo_subaddrIndex": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_TransactionInfo_subaddrAccount": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint32_t
- result: "u32",
- },
- "MONERO_TransactionInfo_label": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_TransactionInfo_confirmations": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_unlockTime": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_hash": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_TransactionInfo_timestamp": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_paymentId": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_TransactionInfo_transfers_count": {
- nonblocking: true,
- // void* txInfo_ptr
- parameters: ["pointer"],
- // int
- result: "i32",
- },
- "MONERO_TransactionInfo_transfers_amount": {
- nonblocking: true,
- // void* txInfo_ptr, int index
- parameters: ["pointer", "i32"],
- // uint64_t
- result: "u64",
- },
- "MONERO_TransactionInfo_transfers_address": {
- nonblocking: true,
- // void* txInfo_ptr, int index
- parameters: ["pointer", "i32"],
- // const char*
- result: "pointer",
- },
- //#endregion
-
- //#region PendingTransaction
- "MONERO_PendingTransaction_status": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // int
- result: "i32",
- },
- "MONERO_PendingTransaction_errorString": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_commit": {
- nonblocking: true,
- // void* pendingTx_ptr, const char* filename, bool overwrite
- parameters: ["pointer", "pointer", "bool"],
- // bool
- result: "bool",
- },
- "MONERO_PendingTransaction_commitUR": {
- nonblocking: true,
- // void* pendingTx_ptr, int max_fragment_length
- parameters: ["pointer", "i32"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_amount": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_PendingTransaction_dust": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_PendingTransaction_fee": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_PendingTransaction_txid": {
- nonblocking: true,
- // void* pendingTx_ptr, const char* separator
- parameters: ["pointer", "pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_txCount": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // uint64_t
- result: "u64",
- },
- "MONERO_PendingTransaction_subaddrAccount": {
- nonblocking: true,
- // void* pendingTx_ptr, const char* separator
- parameters: ["pointer", "pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_subaddrIndices": {
- nonblocking: true,
- // void* pendingTx_ptr, const char* separator
- parameters: ["pointer", "pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_multisigSignData": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_signMultisigTx": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // void
- result: "void",
- },
- "MONERO_PendingTransaction_signersKeys": {
- nonblocking: true,
- // void* pendingTx_ptr
- parameters: ["pointer"],
- // const char*
- result: "pointer",
- },
- "MONERO_PendingTransaction_hex": {
- nonblocking: true,
- // void* pendingTx_ptr, const char* separator
- parameters: ["pointer", "pointer"],
- // const char*
- result: "pointer",
- },
- //#endregion
-
- //#region Checksum
- "MONERO_checksum_wallet2_api_c_h": {
- nonblocking: true,
- parameters: [],
- // const char*
- result: "pointer",
- },
- "MONERO_checksum_wallet2_api_c_cpp": {
- nonblocking: true,
- parameters: [],
- // const char*
- result: "pointer",
- },
- "MONERO_checksum_wallet2_api_c_exp": {
- nonblocking: true,
- parameters: [],
- // const char*
- result: "pointer",
- },
- //#endregion
-
- "MONERO_free": {
- nonblocking: true,
- // void* ptr
- parameters: ["pointer"],
- // void
- result: "void",
- },
-} as const;
-
-type MoneroTsDylib = Deno.DynamicLibrary<typeof symbols>;
-
-export let dylib: MoneroTsDylib;
-export function loadDylib(newDylib?: MoneroTsDylib) {
+export function loadMoneroDylib(newDylib?: MoneroTsDylib) {
if (newDylib) {
dylib = newDylib;
return;
@@ -558,5 +24,30 @@ export function loadDylib(newDylib?: MoneroTsDylib) {
break;
}
- dylib = Deno.dlopen(libPath, symbols);
+ dylib = Deno.dlopen(libPath, moneroSymbols);
+}
+
+export function loadWowneroDylib(newDylib?: WowneroTsDylib) {
+ if (newDylib) {
+ dylib = newDylib;
+ return;
+ }
+
+ let libPath: string;
+ switch (Deno.build.os) {
+ case "darwin":
+ libPath = "./lib/wownero_libwallet2_api_c.dylib";
+ break;
+ case "android":
+ libPath = "./lib/libwownero_libwallet2_api_c.so";
+ break;
+ case "windows":
+ libPath = "./lib/wownero_libwallet2_api_c.dll";
+ break;
+ default:
+ libPath = "./lib/wownero_libwallet2_api_c.so";
+ break;
+ }
+
+ dylib = Deno.dlopen(libPath, wowneroSymbols);
}
diff --git a/impls/monero.ts/src/pending_transaction.ts b/impls/monero.ts/src/pending_transaction.ts
index cf48721..169332f 100644
--- a/impls/monero.ts/src/pending_transaction.ts
+++ b/impls/monero.ts/src/pending_transaction.ts
@@ -1,5 +1,4 @@
-import { dylib } from "./bindings.ts";
-import { CString, readCString, type Sanitizer } from "./utils.ts";
+import { CString, getSymbol, readCString, type Sanitizer } from "./utils.ts";
export type PendingTransactionPtr = Deno.PointerObject<"transactionInfo">;
@@ -13,13 +12,13 @@ export class PendingTransaction {
}
async status(): Promise<number> {
- return await dylib.symbols.MONERO_PendingTransaction_status(this.#pendingTxPtr);
+ return await getSymbol("PendingTransaction_status")(this.#pendingTxPtr);
}
async errorString(): Promise<string | null> {
if (!await this.status()) return null;
- const error = await dylib.symbols.MONERO_PendingTransaction_errorString(this.#pendingTxPtr);
+ const error = await getSymbol("PendingTransaction_errorString")(this.#pendingTxPtr);
if (!error) return null;
return await readCString(error) || null;
@@ -34,7 +33,7 @@ export class PendingTransaction {
}
async commit(fileName: string, overwrite: boolean, sanitize = true): Promise<boolean> {
- const bool = await dylib.symbols.MONERO_PendingTransaction_commit(
+ const bool = await getSymbol("PendingTransaction_commit")(
this.#pendingTxPtr,
CString(fileName),
overwrite,
@@ -44,29 +43,36 @@ export class PendingTransaction {
}
async commitUR(maxFragmentLength: number): Promise<string | null> {
- const result = await dylib.symbols.MONERO_PendingTransaction_commitUR(
+ const commitUR = getSymbol("PendingTransaction_commitUR");
+
+ if (!commitUR) {
+ return null;
+ }
+
+ const result = await commitUR(
this.#pendingTxPtr,
maxFragmentLength,
);
+
if (!result) return null;
await this.throwIfError();
return await readCString(result) || null;
}
async amount(): Promise<bigint> {
- return await dylib.symbols.MONERO_PendingTransaction_amount(this.#pendingTxPtr);
+ return await getSymbol("PendingTransaction_amount")(this.#pendingTxPtr);
}
async dust(): Promise<bigint> {
- return await dylib.symbols.MONERO_PendingTransaction_dust(this.#pendingTxPtr);
+ return await getSymbol("PendingTransaction_dust")(this.#pendingTxPtr);
}
async fee(): Promise<bigint> {
- return await dylib.symbols.MONERO_PendingTransaction_fee(this.#pendingTxPtr);
+ return await getSymbol("PendingTransaction_fee")(this.#pendingTxPtr);
}
async txid(separator: string, sanitize = true): Promise<string | null> {
- const result = await dylib.symbols.MONERO_PendingTransaction_txid(
+ const result = await getSymbol("PendingTransaction_txid")(
this.#pendingTxPtr,
CString(separator),
);
@@ -76,6 +82,6 @@ export class PendingTransaction {
}
async txCount(): Promise<bigint> {
- return await dylib.symbols.MONERO_PendingTransaction_txCount(this.#pendingTxPtr);
+ return await getSymbol("PendingTransaction_txCount")(this.#pendingTxPtr);
}
}
diff --git a/impls/monero.ts/src/symbols.ts b/impls/monero.ts/src/symbols.ts
new file mode 100644
index 0000000..38707b8
--- /dev/null
+++ b/impls/monero.ts/src/symbols.ts
@@ -0,0 +1,553 @@
+export const moneroSymbols = {
+ "MONERO_WalletManagerFactory_getWalletManager": {
+ nonblocking: true,
+ parameters: [],
+ // void*
+ result: "pointer",
+ },
+
+ //#region WalletManager
+ "MONERO_WalletManager_createWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, const char* language, int networkType
+ parameters: ["pointer", "pointer", "pointer", "pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_openWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, int networkType
+ "parameters": ["pointer", "pointer", "pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_recoveryWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, const char* mnemonic,
+ // int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset
+ parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_blockchainHeight": {
+ nonblocking: true,
+ // void* wm_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_WalletManager_blockchainTargetHeight": {
+ nonblocking: true,
+ // void* wm_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_WalletManager_setDaemonAddress": {
+ nonblocking: true,
+ // void* wm_ptr, const char* address
+ parameters: ["pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ //#endregion
+
+ //#region Wallet
+ "MONERO_Wallet_init": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* daemon_address, uint64_t upper_transaction_size_limit,
+ // const char* daemon_username, const char* daemon_password, bool use_ssl, bool lightWallet,
+ // const char* proxy_address
+ parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_init3": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* argv0, const char* default_log_base_name,
+ // const char* log_path, bool console
+ parameters: ["pointer", "pointer", "pointer", "pointer", "bool"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_setTrustedDaemon": {
+ nonblocking: true,
+ // void* wallet_ptr, bool arg
+ parameters: ["pointer", "bool"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_startRefresh": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_refreshAsync": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_blockChainHeight": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_daemonBlockChainHeight": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_synchronized": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_store": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* path
+ parameters: ["pointer", "pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_address": {
+ nonblocking: true,
+ // void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex
+ parameters: ["pointer", "u64", "u64"],
+ // char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_balance": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_unlockedBalance": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_addSubaddressAccount": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* label
+ parameters: ["pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_numSubaddressAccounts": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // size_t
+ result: "usize",
+ },
+ "MONERO_Wallet_addSubaddress": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, const char* label
+ parameters: ["pointer", "u32", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_numSubaddresses": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // size_t
+ result: "usize",
+ },
+ "MONERO_Wallet_getSubaddressLabel": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex
+ parameters: ["pointer", "u32", "u32"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_setSubaddressLabel": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label
+ parameters: ["pointer", "u32", "u32", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_status": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_Wallet_errorString": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_history": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_Wallet_createTransaction": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* dst_addr, const char* payment_id
+ // uint64_t amount, uint32_t mixin_count, int pendingTransactionPriority,
+ // uint32_t subaddr_account, const char* preferredInputs, const char* separator
+ parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_Wallet_amountFromString": {
+ nonblocking: true,
+ // const char* amount
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ //#endregion
+
+ //#region TransactionHistory
+ "MONERO_TransactionHistory_count": {
+ nonblocking: true,
+ // void* txHistory_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionHistory_transaction": {
+ nonblocking: true,
+ // void* txHistory_ptr, int index
+ parameters: ["pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_TransactionHistory_transactionById": {
+ nonblocking: true,
+ // void* txHistory_ptr, const char* id
+ parameters: ["pointer", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_TransactionHistory_refresh": {
+ nonblocking: true,
+ // void* txHistory_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_TransactionHistory_setTxNote": {
+ nonblocking: true,
+ // void* txHistory_ptr, const char* txid, const char* note
+ parameters: ["pointer", "pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ //#endregion
+
+ //#region TransactionInfo
+ "MONERO_TransactionInfo_direction": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionInfo_isPending": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_isFailed": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_isCoinbase": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_amount": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_fee": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_blockHeight": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_description": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_subaddrIndex": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_subaddrAccount": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint32_t
+ result: "u32",
+ },
+ "MONERO_TransactionInfo_label": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_confirmations": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_unlockTime": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_hash": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_timestamp": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_paymentId": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_transfers_count": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionInfo_transfers_amount": {
+ nonblocking: true,
+ // void* txInfo_ptr, int index
+ parameters: ["pointer", "i32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_transfers_address": {
+ nonblocking: true,
+ // void* txInfo_ptr, int index
+ parameters: ["pointer", "i32"],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ //#region PendingTransaction
+ "MONERO_PendingTransaction_status": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_PendingTransaction_errorString": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_commit": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* filename, bool overwrite
+ parameters: ["pointer", "pointer", "bool"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_PendingTransaction_commitUR": {
+ optional: true,
+ nonblocking: true,
+ // void* pendingTx_ptr, int max_fragment_length
+ parameters: ["pointer", "i32"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_amount": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_dust": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_fee": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_txid": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_txCount": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_subaddrAccount": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_subaddrIndices": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_multisigSignData": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_signMultisigTx": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_PendingTransaction_signersKeys": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_hex": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ //#region Checksum
+ "MONERO_checksum_wallet2_api_c_h": {
+ optional: true,
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_checksum_wallet2_api_c_cpp": {
+ optional: true,
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_checksum_wallet2_api_c_exp": {
+ optional: true,
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ "MONERO_free": {
+ nonblocking: true,
+ // void* ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+} as const;
+
+type MoneroSymbols = typeof moneroSymbols;
+export type MoneroTsDylib = Deno.DynamicLibrary<MoneroSymbols>;
+
+type ReplaceMonero<T extends string> = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never;
+
+type WowneroSymbols = { [Key in keyof MoneroSymbols as ReplaceMonero<Key>]: MoneroSymbols[Key] };
+
+export const wowneroSymbols = Object.fromEntries(
+ Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]),
+) as WowneroSymbols;
+
+export type WowneroTsDylib = Deno.DynamicLibrary<WowneroSymbols>;
+
+export type Dylib = MoneroTsDylib | WowneroTsDylib;
diff --git a/impls/monero.ts/src/transaction_history.ts b/impls/monero.ts/src/transaction_history.ts
index cc76fc2..aab64da 100644
--- a/impls/monero.ts/src/transaction_history.ts
+++ b/impls/monero.ts/src/transaction_history.ts
@@ -1,6 +1,5 @@
-import { dylib } from "./bindings.ts";
import { TransactionInfo, TransactionInfoPtr } from "./transaction_info.ts";
-import { CString } from "./utils.ts";
+import { CString, getSymbol } from "./utils.ts";
export type TransactionHistoryPtr = Deno.PointerObject<"transactionHistory">;
@@ -12,24 +11,23 @@ export class TransactionHistory {
}
async count(): Promise<number> {
- return await dylib.symbols.MONERO_TransactionHistory_count(this.#txHistoryPtr);
+ return await getSymbol("TransactionHistory_count")(this.#txHistoryPtr);
}
async transaction(index: number): Promise<TransactionInfo> {
return new TransactionInfo(
- (await dylib.symbols.MONERO_TransactionHistory_transaction(
- this.#txHistoryPtr,
- index,
- )) as TransactionInfoPtr,
+ (
+ await getSymbol("TransactionHistory_transaction")(this.#txHistoryPtr, index)
+ ) as TransactionInfoPtr,
);
}
async refresh(): Promise<void> {
- await dylib.symbols.MONERO_TransactionHistory_refresh(this.#txHistoryPtr);
+ await getSymbol("TransactionHistory_refresh")(this.#txHistoryPtr);
}
async setTxNote(transactionId: string, note: string): Promise<void> {
- await dylib.symbols.MONERO_TransactionHistory_setTxNote(
+ await getSymbol("TransactionHistory_setTxNote")(
this.#txHistoryPtr,
CString(transactionId),
CString(note),
diff --git a/impls/monero.ts/src/transaction_info.ts b/impls/monero.ts/src/transaction_info.ts
index 7db45f5..22ea0e7 100644
--- a/impls/monero.ts/src/transaction_info.ts
+++ b/impls/monero.ts/src/transaction_info.ts
@@ -1,5 +1,5 @@
import { dylib } from "./bindings.ts";
-import { readCString, Sanitizer } from "./utils.ts";
+import { getSymbol, readCString, Sanitizer } from "./utils.ts";
export type TransactionInfoPtr = Deno.PointerObject<"transactionInfo">;
@@ -13,7 +13,7 @@ export class TransactionInfo {
}
async direction(): Promise<"in" | "out"> {
- switch (await dylib.symbols.MONERO_TransactionInfo_direction(this.#txInfoPtr)) {
+ switch (await getSymbol("TransactionInfo_direction")(this.#txInfoPtr)) {
case 0:
return "in";
case 1:
@@ -25,80 +25,80 @@ export class TransactionInfo {
}
async isPending(): Promise<boolean> {
- return await dylib.symbols.MONERO_TransactionInfo_isPending(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_isPending")(this.#txInfoPtr);
}
async isFailed(): Promise<boolean> {
- return await dylib.symbols.MONERO_TransactionInfo_isFailed(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_isFailed")(this.#txInfoPtr);
}
async isCoinbase(): Promise<boolean> {
- return await dylib.symbols.MONERO_TransactionInfo_isCoinbase(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_isCoinbase")(this.#txInfoPtr);
}
async amount(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_amount(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_amount")(this.#txInfoPtr);
}
async fee(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_fee(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_fee")(this.#txInfoPtr);
}
async blockHeight(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_blockHeight(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_blockHeight")(this.#txInfoPtr);
}
async description(): Promise<string> {
- const description = await dylib.symbols.MONERO_TransactionInfo_description(this.#txInfoPtr);
+ const description = await getSymbol("TransactionInfo_description")(this.#txInfoPtr);
return await readCString(description) || "";
}
async subaddrIndex(): Promise<string> {
- const subaddrIndex = await dylib.symbols.MONERO_TransactionInfo_subaddrIndex(this.#txInfoPtr);
+ const subaddrIndex = await getSymbol("TransactionInfo_subaddrIndex")(this.#txInfoPtr);
return await readCString(subaddrIndex) || "";
}
async subaddrAccount(): Promise<number> {
- return await dylib.symbols.MONERO_TransactionInfo_subaddrAccount(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_subaddrAccount")(this.#txInfoPtr);
}
async label(): Promise<string> {
- const label = await dylib.symbols.MONERO_TransactionInfo_label(this.#txInfoPtr);
+ const label = await getSymbol("TransactionInfo_label")(this.#txInfoPtr);
return await readCString(label) || "";
}
async confirmations(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_confirmations(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_confirmations")(this.#txInfoPtr);
}
async unlockTime(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_unlockTime(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_unlockTime")(this.#txInfoPtr);
}
async hash(): Promise<string> {
- const hash = await dylib.symbols.MONERO_TransactionInfo_hash(this.#txInfoPtr);
+ const hash = await getSymbol("TransactionInfo_hash")(this.#txInfoPtr);
return await readCString(hash) || "";
}
async timestamp(): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_timestamp(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_timestamp")(this.#txInfoPtr);
}
async paymentId(): Promise<string> {
- const paymentId = await dylib.symbols.MONERO_TransactionInfo_paymentId(this.#txInfoPtr);
+ const paymentId = await getSymbol("TransactionInfo_paymentId")(this.#txInfoPtr);
return await readCString(paymentId) || "";
}
async transfersCount(): Promise<number> {
- return await dylib.symbols.MONERO_TransactionInfo_transfers_count(this.#txInfoPtr);
+ return await getSymbol("TransactionInfo_transfers_count")(this.#txInfoPtr);
}
async transfersAmount(index: number): Promise<bigint> {
- return await dylib.symbols.MONERO_TransactionInfo_transfers_amount(this.#txInfoPtr, index);
+ return await getSymbol("TransactionInfo_transfers_amount")(this.#txInfoPtr, index);
}
async transfersAddress(index: number): Promise<string> {
- const transfersAddress = await dylib.symbols.MONERO_TransactionInfo_transfers_address(this.#txInfoPtr, index);
+ const transfersAddress = await getSymbol("TransactionInfo_transfers_address")(this.#txInfoPtr, index);
return await readCString(transfersAddress) || "";
}
}
diff --git a/impls/monero.ts/src/utils.ts b/impls/monero.ts/src/utils.ts
index 6fa640f..e88ddcd 100644
--- a/impls/monero.ts/src/utils.ts
+++ b/impls/monero.ts/src/utils.ts
@@ -1,4 +1,5 @@
import { dylib } from "../mod.ts";
+import type { moneroSymbols, MoneroTsDylib, WowneroTsDylib } from "./symbols.ts";
export type Sanitizer = () => void | PromiseLike<void>;
@@ -7,6 +8,17 @@ export function CString(string: string): Deno.PointerValue<string> {
return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`));
}
+type SymbolWithoutPrefix = keyof typeof moneroSymbols extends `MONERO_${infer DylibSymbol}` ? DylibSymbol : never;
+export function getSymbol<S extends SymbolWithoutPrefix>(
+ symbol: S,
+): MoneroTsDylib["symbols"][`MONERO_${S}`] | WowneroTsDylib["symbols"][`WOWNERO_${S}`] {
+ if ("MONERO_free" in dylib.symbols) {
+ return dylib.symbols[`MONERO_${symbol}` as const];
+ } else {
+ return dylib.symbols[`WOWNERO_${symbol}` as const];
+ }
+}
+
/**
* This method reads string from the given pointer and frees the string.
*
@@ -19,7 +31,7 @@ export async function readCString(pointer: Deno.PointerValue, free = true): Prom
if (!pointer) return null;
const string = new Deno.UnsafePointerView(pointer).getCString();
if (free) {
- await dylib.symbols.MONERO_free(pointer);
+ await getSymbol("free")(pointer);
}
return string;
}
diff --git a/impls/monero.ts/src/wallet.ts b/impls/monero.ts/src/wallet.ts
index 07c40ce..ea25f21 100644
--- a/impls/monero.ts/src/wallet.ts
+++ b/impls/monero.ts/src/wallet.ts
@@ -1,8 +1,8 @@
import { dylib } from "./bindings.ts";
-import { CString, readCString, Sanitizer } from "./utils.ts";
+import { CString, getSymbol, readCString, Sanitizer } from "./utils.ts";
+
import { WalletManager, type WalletManagerPtr } from "./wallet_manager.ts";
import { TransactionHistory, TransactionHistoryPtr } from "./transaction_history.ts";
-
import { PendingTransaction } from "./pending_transaction.ts";
import { PendingTransactionPtr } from "./pending_transaction.ts";
@@ -19,40 +19,44 @@ export class Wallet {
this.sanitizer = sanitizer;
}
+ getPointer(): WalletPtr {
+ return this.#walletPtr;
+ }
+
async store(path = ""): Promise<boolean> {
- const bool = await dylib.symbols.MONERO_Wallet_store(this.#walletPtr, CString(path));
+ const bool = await getSymbol("Wallet_store")(this.#walletPtr, CString(path));
await this.throwIfError();
return bool;
}
- async initWallet(): Promise<void> {
+ async initWallet(daemonAddress = "http://nodex.monerujo.io:18081"): Promise<void> {
await this.init();
await this.setTrustedDaemon(true);
- await this.setDaemonAddress("http://nodex.monerujo.io:18081");
+ await this.setDaemonAddress(daemonAddress);
await this.startRefresh();
await this.refreshAsync();
await this.throwIfError();
}
async setDaemonAddress(address: string): Promise<void> {
- await dylib.symbols.MONERO_WalletManager_setDaemonAddress(
+ await getSymbol("WalletManager_setDaemonAddress")(
this.#walletManagerPtr,
CString(address),
);
}
async startRefresh(): Promise<void> {
- await dylib.symbols.MONERO_Wallet_startRefresh(this.#walletPtr);
+ await getSymbol("Wallet_startRefresh")(this.#walletPtr);
await this.throwIfError();
}
async refreshAsync(): Promise<void> {
- await dylib.symbols.MONERO_Wallet_refreshAsync(this.#walletPtr);
+ await getSymbol("Wallet_refreshAsync")(this.#walletPtr);
await this.throwIfError();
}
async init(): Promise<boolean> {
- const bool = await dylib.symbols.MONERO_Wallet_init(
+ const bool = await getSymbol("Wallet_init")(
this.#walletPtr,
CString("http://nodex.monerujo.io:18081"),
0n,
@@ -67,7 +71,7 @@ export class Wallet {
}
async setTrustedDaemon(value: boolean): Promise<void> {
- await dylib.symbols.MONERO_Wallet_setTrustedDaemon(this.#walletPtr, value);
+ await getSymbol("Wallet_setTrustedDaemon")(this.#walletPtr, value);
}
static async create(
@@ -79,7 +83,7 @@ export class Wallet {
// We assign holder of the pointer in Wallet constructor
const walletManagerPtr = walletManager.getPointer();
- const walletPtr = await dylib.symbols.MONERO_WalletManager_createWallet(
+ const walletPtr = await getSymbol("WalletManager_createWallet")(
walletManagerPtr,
CString(path),
CString(password),
@@ -103,7 +107,7 @@ export class Wallet {
// We assign holder of the pointer in Wallet constructor
const walletManagerPtr = walletManager.getPointer();
- const walletPtr = await dylib.symbols.MONERO_WalletManager_openWallet(
+ const walletPtr = await getSymbol("WalletManager_openWallet")(
walletManagerPtr,
CString(path),
CString(password),
@@ -129,7 +133,7 @@ export class Wallet {
// We assign holder of the pointer in Wallet constructor
const walletManagerPtr = walletManager.getPointer();
- const walletPtr = await dylib.symbols.MONERO_WalletManager_recoveryWallet(
+ const walletPtr = await getSymbol("WalletManager_recoveryWallet")(
walletManagerPtr,
CString(path),
CString(password),
@@ -148,7 +152,7 @@ export class Wallet {
}
async address(accountIndex = 0n, addressIndex = 0n): Promise<string> {
- const address = await dylib.symbols.MONERO_Wallet_address(this.#walletPtr, accountIndex, addressIndex);
+ const address = await getSymbol("Wallet_address")(this.#walletPtr, accountIndex, addressIndex);
if (!address) {
const error = await this.errorString();
throw new Error(`Failed getting address from a wallet: ${error ?? "<Error unknown>"}`);
@@ -157,21 +161,21 @@ export class Wallet {
}
async balance(accountIndex = 0): Promise<bigint> {
- return await dylib.symbols.MONERO_Wallet_balance(this.#walletPtr, accountIndex);
+ return await getSymbol("Wallet_balance")(this.#walletPtr, accountIndex);
}
async unlockedBalance(accountIndex = 0): Promise<bigint> {
- return await dylib.symbols.MONERO_Wallet_unlockedBalance(this.#walletPtr, accountIndex);
+ return await getSymbol("Wallet_unlockedBalance")(this.#walletPtr, accountIndex);
}
status(): Promise<number> {
- return dylib.symbols.MONERO_Wallet_status(this.#walletPtr);
+ return getSymbol("Wallet_status")(this.#walletPtr);
}
async errorString(): Promise<string | null> {
if (!await this.status()) return null;
- const error = await dylib.symbols.MONERO_Wallet_errorString(this.#walletPtr);
+ const error = await getSymbol("Wallet_errorString")(this.#walletPtr);
if (!error) return null;
return await readCString(error) || null;
@@ -186,37 +190,37 @@ export class Wallet {
}
async synchronized(): Promise<boolean> {
- const synchronized = await dylib.symbols.MONERO_Wallet_synchronized(this.#walletPtr);
+ const synchronized = await getSymbol("Wallet_synchronized")(this.#walletPtr);
await this.throwIfError();
return synchronized;
}
async blockChainHeight(): Promise<bigint> {
- const height = await dylib.symbols.MONERO_Wallet_blockChainHeight(this.#walletPtr);
+ const height = await getSymbol("Wallet_blockChainHeight")(this.#walletPtr);
await this.throwIfError();
return height;
}
async daemonBlockChainHeight(): Promise<bigint> {
- const height = await dylib.symbols.MONERO_Wallet_daemonBlockChainHeight(this.#walletPtr);
+ const height = await getSymbol("Wallet_daemonBlockChainHeight")(this.#walletPtr);
await this.throwIfError();
return height;
}
async managerBlockChainHeight(): Promise<bigint> {
- const height = await dylib.symbols.MONERO_WalletManager_blockchainHeight(this.#walletManagerPtr);
+ const height = await getSymbol("WalletManager_blockchainHeight")(this.#walletManagerPtr);
await this.throwIfError();
return height;
}
async managerTargetBlockChainHeight(): Promise<bigint> {
- const height = await dylib.symbols.MONERO_WalletManager_blockchainTargetHeight(this.#walletManagerPtr);
+ const height = await getSymbol("WalletManager_blockchainTargetHeight")(this.#walletManagerPtr);
await this.throwIfError();
return height;
}
async addSubaddressAccount(label: string): Promise<void> {
- await dylib.symbols.MONERO_Wallet_addSubaddressAccount(
+ await getSymbol("Wallet_addSubaddressAccount")(
this.#walletPtr,
CString(label),
);
@@ -224,13 +228,13 @@ export class Wallet {
}
async numSubaddressAccounts(): Promise<bigint> {
- const accountsLen = await dylib.symbols.MONERO_Wallet_numSubaddressAccounts(this.#walletPtr);
+ const accountsLen = await getSymbol("Wallet_numSubaddressAccounts")(this.#walletPtr);
await this.throwIfError();
return accountsLen;
}
async addSubaddress(accountIndex: number, label: string): Promise<void> {
- await dylib.symbols.MONERO_Wallet_addSubaddress(
+ await getSymbol("Wallet_addSubaddress")(
this.#walletPtr,
accountIndex,
CString(label),
@@ -239,7 +243,7 @@ export class Wallet {
}
async numSubaddresses(accountIndex: number): Promise<bigint> {
- const address = await dylib.symbols.MONERO_Wallet_numSubaddresses(
+ const address = await getSymbol("Wallet_numSubaddresses")(
this.#walletPtr,
accountIndex,
);
@@ -248,7 +252,7 @@ export class Wallet {
}
async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise<string> {
- const label = await dylib.symbols.MONERO_Wallet_getSubaddressLabel(this.#walletPtr, accountIndex, addressIndex);
+ const label = await getSymbol("Wallet_getSubaddressLabel")(this.#walletPtr, accountIndex, addressIndex);
if (!label) {
const error = await this.errorString();
throw new Error(`Failed getting subaddress label from a wallet: ${error ?? "<Error unknown>"}`);
@@ -257,7 +261,7 @@ export class Wallet {
}
async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise<void> {
- await dylib.symbols.MONERO_Wallet_setSubaddressLabel(
+ await getSymbol("Wallet_setSubaddressLabel")(
this.#walletPtr,
accountIndex,
addressIndex,
@@ -267,7 +271,7 @@ export class Wallet {
}
async getHistory(): Promise<TransactionHistory> {
- const transactionHistoryPointer = await dylib.symbols.MONERO_Wallet_history(this.#walletPtr);
+ const transactionHistoryPointer = await getSymbol("Wallet_history")(this.#walletPtr);
await this.throwIfError();
return new TransactionHistory(transactionHistoryPointer as TransactionHistoryPtr);
}
@@ -283,7 +287,7 @@ export class Wallet {
paymentId = "",
separator = ",",
): Promise<PendingTransaction> {
- const pendingTxPtr = await dylib.symbols.MONERO_Wallet_createTransaction(
+ const pendingTxPtr = await getSymbol("Wallet_createTransaction")(
this.#walletPtr,
CString(destinationAddress),
CString(paymentId),
@@ -299,6 +303,6 @@ export class Wallet {
}
async amountFromString(amount: string): Promise<bigint> {
- return await dylib.symbols.MONERO_Wallet_amountFromString(CString(amount));
+ return await getSymbol("Wallet_amountFromString")(CString(amount));
}
}
diff --git a/impls/monero.ts/src/wallet_manager.ts b/impls/monero.ts/src/wallet_manager.ts
index ad9cf31..b1fafec 100644
--- a/impls/monero.ts/src/wallet_manager.ts
+++ b/impls/monero.ts/src/wallet_manager.ts
@@ -1,5 +1,4 @@
-import { dylib } from "./bindings.ts";
-import { Sanitizer } from "./utils.ts";
+import { getSymbol, Sanitizer } from "./utils.ts";
export type WalletManagerPtr = Deno.PointerObject<"walletManager">;
@@ -17,7 +16,7 @@ export class WalletManager {
}
static async new(sanitizer?: Sanitizer) {
- const ptr = await dylib.symbols.MONERO_WalletManagerFactory_getWalletManager();
+ const ptr = await getSymbol("WalletManagerFactory_getWalletManager")();
if (!ptr) {
sanitizer?.();
throw new Error("Failed retrieving wallet manager");
diff --git a/monero_libwallet2_api_c/CMakeLists.txt b/monero_libwallet2_api_c/CMakeLists.txt
index eb1714e..60be91c 100644
--- a/monero_libwallet2_api_c/CMakeLists.txt
+++ b/monero_libwallet2_api_c/CMakeLists.txt
@@ -32,6 +32,7 @@ if(${HOST_ABI} STREQUAL "x86_64-linux-android" OR
${HOST_ABI} STREQUAL "aarch64-linux-android" OR
${HOST_ABI} STREQUAL "armv7a-linux-androideabi")
add_link_options(-stdlib=libc++ -static-libstdc++)
+ set(EXTRA_LIBS_ANDROID log)
endif()
add_library( wallet2_api_c
diff --git a/monero_libwallet2_api_c/src/main/cpp/helpers.cpp b/monero_libwallet2_api_c/src/main/cpp/helpers.cpp
index d692879..8e45eba 100644
--- a/monero_libwallet2_api_c/src/main/cpp/helpers.cpp
+++ b/monero_libwallet2_api_c/src/main/cpp/helpers.cpp
@@ -9,6 +9,71 @@
#include <set>
#include <sstream>
#include <cstring>
+#include <thread>
+
+#ifdef __ANDROID__
+#include <android/log.h>
+
+#define LOG_TAG "moneroc"
+#define BUFFER_SIZE 1024*32
+
+static int stdoutToLogcat(const char *buf, int size) {
+ __android_log_write(ANDROID_LOG_INFO, LOG_TAG, buf);
+ return size;
+}
+
+static int stderrToLogcat(const char *buf, int size) {
+ __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, buf);
+ return size;
+}
+
+void redirectStdoutThread(int pipe_stdout[2]) {
+ char bufferStdout[BUFFER_SIZE];
+ while (true) {
+ int read_size = read(pipe_stdout[0], bufferStdout, sizeof(bufferStdout) - 1);
+ if (read_size > 0) {
+ bufferStdout[read_size] = '\0';
+ stdoutToLogcat(bufferStdout, read_size);
+ }
+ }
+}
+
+void redirectStderrThread(int pipe_stderr[2]) {
+ char bufferStderr[BUFFER_SIZE];
+ while (true) {
+ int read_size = read(pipe_stderr[0], bufferStderr, sizeof(bufferStderr) - 1);
+ if (read_size > 0) {
+ bufferStderr[read_size] = '\0';
+ stderrToLogcat(bufferStderr, read_size);
+ }
+ }
+}
+
+void setupAndroidLogging() {
+ static int pfdStdout[2];
+ static int pfdStderr[2];
+
+ pipe(pfdStdout);
+ pipe(pfdStderr);
+
+ dup2(pfdStdout[1], STDOUT_FILENO);
+ dup2(pfdStderr[1], STDERR_FILENO);
+
+ std::thread stdoutThread(redirectStdoutThread, pfdStdout);
+ std::thread stderrThread(redirectStderrThread, pfdStderr);
+
+ stdoutThread.detach();
+ stderrThread.detach();
+}
+
+#endif // __ANDROID__
+
+__attribute__((constructor))
+void library_init() {
+#ifdef __ANDROID__
+ setupAndroidLogging(); // This will now run automatically when the library is loaded
+#endif
+}
const char* vectorToString(const std::vector<std::string>& vec, const std::string separator) {
// Check if the vector is empty
@@ -195,4 +260,4 @@ std::vector<uint64_t> splitStringUint(const std::string& str, const std::string&
}
tokens.push_back(std::stoull(content)); // Inserting the last token
return tokens;
-} \ No newline at end of file
+}
diff --git a/patches/monero/0019-fix-for-coin-control-patch.patch b/patches/monero/0019-fix-for-coin-control-patch.patch
new file mode 100644
index 0000000..b7ff7f9
--- /dev/null
+++ b/patches/monero/0019-fix-for-coin-control-patch.patch
@@ -0,0 +1,97 @@
+From 8f93306ed526e0e573b33fc7dd40abbba7e7a00a Mon Sep 17 00:00:00 2001
+From: Czarek Nakamoto <cyjan@mrcyjanek.net>
+Date: Tue, 15 Oct 2024 18:00:05 +0200
+Subject: [PATCH] fix for coin control patch
+
+---
+ src/wallet/api/coins.cpp | 1 +
+ src/wallet/api/wallet.cpp | 36 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 36 insertions(+), 1 deletion(-)
+
+diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp
+index 2321c638d..ef12141cf 100644
+--- a/src/wallet/api/coins.cpp
++++ b/src/wallet/api/coins.cpp
+@@ -114,6 +114,7 @@ void CoinsImpl::setFrozen(int index)
+ {
+ try
+ {
++ LOG_ERROR("Freezing coin: " << index);
+ m_wallet->m_wallet->freeze(index);
+ refresh();
+ }
+diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
+index ec7d60ec0..db127dae4 100644
+--- a/src/wallet/api/wallet.cpp
++++ b/src/wallet/api/wallet.cpp
+@@ -2116,6 +2116,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
+ }
+ }
+ bool error = false;
++ uint64_t amountSum = 0;
+ for (size_t i = 0; i < dst_addr.size() && !error; i++) {
+ if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
+ // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
+@@ -2137,6 +2138,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
+ de.original = dst_addr[i];
+ de.addr = info.address;
+ de.amount = (*amount)[i];
++ amountSum += (*amount)[i];
+ de.is_subaddress = info.is_subaddress;
+ de.is_integrated = info.has_payment_id;
+ dsts.push_back(de);
+@@ -2147,18 +2149,50 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
+ }
+ }
+ }
++ // uint64_t maxAllowedSpend = m_wallet->unlocked_balance(subaddr_account, true);
++ // if (maxAllowedSpend < amountSum) {
++ // error = true;
++ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount"));
++ // break;
++ // }
+ std::vector<crypto::key_image> preferred_input_list;
+ if (!preferred_inputs.empty()) {
++ LOG_ERROR("empty");
++
+ for (const auto &public_key : preferred_inputs) {
+ crypto::key_image keyImage;
+ bool r = epee::string_tools::hex_to_pod(public_key, keyImage);
+- if (!r) {
++ if (!r) {
+ error = true;
+ setStatusError(tr("failed to parse key image"));
+ break;
+ }
++ if (m_wallet->frozen(keyImage)) {
++ error = true;
++ setStatusError(tr("refusing to spend frozen coin"));
++ break;
++ }
++
+ preferred_input_list.push_back(keyImage);
+ }
++ } else {
++ LOG_ERROR("not empty");
++
++ boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_transfers_mutex);
++ for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) {
++ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
++ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<<td.m_spent << ";" << td.m_frozen << ";" << m_wallet->frozen(td));
++ if (td.m_spent) continue;
++ LOG_ERROR("is frozen");
++ if (!td.m_frozen) {
++ LOG_ERROR("isn't:");
++ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount());
++ preferred_input_list.push_back(td.m_key_image);
++ }
++ }
++ }
++ for (const auto &de : preferred_input_list) {
++ LOG_ERROR("preferred input: " << de);
+ }
+ if (error) {
+ break;
+--
+2.39.5 (Apple Git-154)
+
diff --git a/tests/compare.ts b/tests/compare.ts
new file mode 100755
index 0000000..d09bdd9
--- /dev/null
+++ b/tests/compare.ts
@@ -0,0 +1,80 @@
+import { moneroSymbols as symbols, type MoneroTsDylib, type WowneroTsDylib } from "../impls/monero.ts/src/symbols.ts";
+import { loadMoneroDylib, loadWowneroDylib } from "../impls/monero.ts/src/bindings.ts";
+import { Wallet, WalletManager } from "../impls/monero.ts/mod.ts";
+import { readCString } from "../impls/monero.ts/src/utils.ts";
+import { assertEquals } from "jsr:@std/assert";
+
+const coin = Deno.args[0] as "monero" | "wownero";
+const version = Deno.args[1];
+const walletInfo = JSON.parse(Deno.args[2]);
+
+const moneroSymbols = {
+ ...symbols,
+
+ "MONERO_Wallet_secretViewKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_publicViewKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+
+ "MONERO_Wallet_secretSpendKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_publicSpendKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+} as const;
+
+type ReplaceMonero<T extends string> = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never;
+type WowneroSymbols = { [Key in keyof typeof moneroSymbols as ReplaceMonero<Key>]: (typeof moneroSymbols)[Key] };
+const wowneroSymbols = Object.fromEntries(
+ Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]),
+) as WowneroSymbols;
+
+let getKey: (wallet: Wallet, type: `${"secret" | "public"}${"Spend" | "View"}Key`) => Promise<string | null>;
+
+if (coin === "monero") {
+ const dylib = Deno.dlopen(`tests/libs/${version}/monero_libwallet2_api_c.so`, moneroSymbols);
+ loadMoneroDylib(dylib as MoneroTsDylib);
+
+ getKey = async (wallet, type) =>
+ await readCString(await dylib.symbols[`MONERO_Wallet_${type}` as const](wallet.getPointer()));
+} else {
+ const dylib = Deno.dlopen(`tests/libs/${version}/wownero_libwallet2_api_c.so`, wowneroSymbols);
+ loadWowneroDylib(dylib as WowneroTsDylib);
+
+ getKey = async (wallet, type) =>
+ await readCString(
+ await dylib.symbols[`WOWNERO_Wallet_${type}` as const](wallet.getPointer()),
+ );
+}
+
+const walletManager = await WalletManager.new();
+const wallet = await Wallet.open(walletManager, walletInfo.path, walletInfo.password);
+
+assertEquals(await wallet.address(), walletInfo.address);
+
+assertEquals(await getKey(wallet, "publicSpendKey"), walletInfo.publicSpendKey);
+assertEquals(await getKey(wallet, "secretSpendKey"), walletInfo.secretSpendKey);
+
+assertEquals(await getKey(wallet, "publicViewKey"), walletInfo.publicViewKey);
+assertEquals(await getKey(wallet, "secretViewKey"), walletInfo.secretViewKey);
+
+await wallet.store(walletInfo.path);
diff --git a/tests/deno.jsonc b/tests/deno.jsonc
new file mode 100755
index 0000000..a7b75ec
--- /dev/null
+++ b/tests/deno.jsonc
@@ -0,0 +1,5 @@
+{
+ "fmt": {
+ "lineWidth": 120
+ }
+}
diff --git a/tests/deno.lock b/tests/deno.lock
new file mode 100755
index 0000000..02d189f
--- /dev/null
+++ b/tests/deno.lock
@@ -0,0 +1,90 @@
+{
+ "version": "4",
+ "specifiers": {
+ "jsr:@david/dax@*": "0.42.0",
+ "jsr:@david/path@0.2": "0.2.0",
+ "jsr:@david/which@~0.4.1": "0.4.1",
+ "jsr:@std/assert@*": "0.221.0",
+ "jsr:@std/assert@0.221": "0.221.0",
+ "jsr:@std/bytes@0.221": "0.221.0",
+ "jsr:@std/fmt@0.221": "0.221.0",
+ "jsr:@std/fmt@1": "1.0.2",
+ "jsr:@std/fs@1": "1.0.4",
+ "jsr:@std/io@0.221": "0.221.0",
+ "jsr:@std/path@1": "1.0.6",
+ "jsr:@std/path@^1.0.6": "1.0.6",
+ "jsr:@std/streams@0.221": "0.221.0",
+ "jsr:@std/streams@^1.0.7": "1.0.7",
+ "jsr:@std/tar@*": "0.1.2"
+ },
+ "jsr": {
+ "@david/dax@0.42.0": {
+ "integrity": "0c547c9a20577a6072b90def194c159c9ddab82280285ebfd8268a4ebefbd80b",
+ "dependencies": [
+ "jsr:@david/path",
+ "jsr:@david/which",
+ "jsr:@std/fmt@1",
+ "jsr:@std/fs",
+ "jsr:@std/io",
+ "jsr:@std/path@1",
+ "jsr:@std/streams@0.221"
+ ]
+ },
+ "@david/path@0.2.0": {
+ "integrity": "f2d7aa7f02ce5a55e27c09f9f1381794acb09d328f8d3c8a2e3ab3ffc294dccd",
+ "dependencies": [
+ "jsr:@std/fs",
+ "jsr:@std/path@1"
+ ]
+ },
+ "@david/which@0.4.1": {
+ "integrity": "896a682b111f92ab866cc70c5b4afab2f5899d2f9bde31ed00203b9c250f225e"
+ },
+ "@std/assert@0.221.0": {
+ "integrity": "a5f1aa6e7909dbea271754fd4ab3f4e687aeff4873b4cef9a320af813adb489a",
+ "dependencies": [
+ "jsr:@std/fmt@0.221"
+ ]
+ },
+ "@std/bytes@0.221.0": {
+ "integrity": "64a047011cf833890a4a2ab7293ac55a1b4f5a050624ebc6a0159c357de91966"
+ },
+ "@std/fmt@0.221.0": {
+ "integrity": "379fed69bdd9731110f26b9085aeb740606b20428ce6af31ef6bd45ef8efa62a"
+ },
+ "@std/fmt@1.0.2": {
+ "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7"
+ },
+ "@std/fs@1.0.4": {
+ "integrity": "2907d32d8d1d9e540588fd5fe0ec21ee638134bd51df327ad4e443aaef07123c",
+ "dependencies": [
+ "jsr:@std/path@^1.0.6"
+ ]
+ },
+ "@std/io@0.221.0": {
+ "integrity": "faf7f8700d46ab527fa05cc6167f4b97701a06c413024431c6b4d207caa010da",
+ "dependencies": [
+ "jsr:@std/assert@0.221",
+ "jsr:@std/bytes"
+ ]
+ },
+ "@std/path@1.0.6": {
+ "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed"
+ },
+ "@std/streams@0.221.0": {
+ "integrity": "47f2f74634b47449277c0ee79fe878da4424b66bd8975c032e3afdca88986e61",
+ "dependencies": [
+ "jsr:@std/io"
+ ]
+ },
+ "@std/streams@1.0.7": {
+ "integrity": "1a93917ca0c58c01b2bfb93647189229b1702677f169b6fb61ad6241cd2e499b"
+ },
+ "@std/tar@0.1.2": {
+ "integrity": "98183102395decd6268253996177804f818580ef547a25b81da0e7cc334db708",
+ "dependencies": [
+ "jsr:@std/streams@^1.0.7"
+ ]
+ }
+ }
+}
diff --git a/tests/regression.test.ts b/tests/regression.test.ts
new file mode 100755
index 0000000..82a9f95
--- /dev/null
+++ b/tests/regression.test.ts
@@ -0,0 +1,39 @@
+import { $, createWalletViaCli, downloadCli, getMoneroC, getMoneroCTags } from "./utils.ts";
+
+const coin = Deno.env.get("COIN");
+if (coin !== "monero" && coin !== "wownero") {
+ throw new Error("COIN env var invalid or missing");
+}
+
+Deno.test(`Regression tests (${coin})`, async (t) => {
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ await Deno.mkdir("./tests/wallets", { recursive: true });
+
+ const tags = await getMoneroCTags();
+ const latestTag = tags[0];
+ await Promise.all([getMoneroC(coin, "next"), await getMoneroC(coin, latestTag), downloadCli(coin)]);
+
+ await t.step("Simple (next, latest, next)", async () => {
+ const walletInfo = await createWalletViaCli(coin, "dog", "sobaka");
+
+ for (const version of ["next", latestTag, "next"]) {
+ await $`deno run -A ./tests/compare.ts ${coin} ${version} ${JSON.stringify(walletInfo)}`;
+ }
+ });
+
+ await t.step("All releases sequentially (all tags in the release order, next)", async () => {
+ tags.unshift("next");
+
+ const walletInfo = await createWalletViaCli(coin, "cat", "koshka");
+
+ for (const version of tags.toReversed()) {
+ if (version !== "next" && version !== tags[0]) await getMoneroC(coin, version);
+ await $`deno run -A ./tests/compare.ts ${coin} ${version} ${JSON.stringify(walletInfo)}`;
+ }
+
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ });
+
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ await Deno.remove("./tests/libs", { recursive: true }).catch(() => {});
+});
diff --git a/tests/utils.ts b/tests/utils.ts
new file mode 100755
index 0000000..028e0ff
--- /dev/null
+++ b/tests/utils.ts
@@ -0,0 +1,131 @@
+import { build$, CommandBuilder } from "jsr:@david/dax";
+
+export const $ = build$({
+ commandBuilder: new CommandBuilder()
+ .printCommand(true)
+ .stdin("inherit")
+ .stdout("inherit")
+ .stderr("inherit"),
+});
+
+type Coin = "monero" | "wownero";
+
+export async function downloadMoneroCli() {
+ const MONERO_CLI_FILE_NAME = "monero-linux-x64-v0.18.3.4";
+ const MONERO_WALLET_CLI_URL = `https://downloads.getmonero.org/cli/${MONERO_CLI_FILE_NAME}.tar.bz2`;
+
+ await $`wget ${MONERO_WALLET_CLI_URL}`;
+ await $
+ .raw`tar -xvf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`;
+ await $.raw`rm ${MONERO_CLI_FILE_NAME}.tar.bz2`;
+}
+
+export async function downloadWowneroCli() {
+ const WOWNERO_CLI_FILE_NAME = "wownero-x86_64-linux-gnu-59db3fe8d";
+ const WOWNERO_WALLET_CLI_URL =
+ `https://codeberg.org/wownero/wownero/releases/download/v0.11.2.0/wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2`;
+
+ await $`wget ${WOWNERO_WALLET_CLI_URL}`;
+ await $
+ .raw`tar -xvf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`;
+ await $.raw`rm ${WOWNERO_CLI_FILE_NAME}.tar.bz2`;
+}
+
+export function downloadCli(coin: Coin) {
+ if (coin === "wownero") {
+ return downloadWowneroCli();
+ }
+ return downloadMoneroCli();
+}
+
+interface WalletInfo {
+ path: string;
+ password: string;
+ address: string;
+ secretSpendKey: string;
+ publicSpendKey: string;
+ secretViewKey: string;
+ publicViewKey: string;
+}
+
+export async function createWalletViaCli(
+ coin: Coin,
+ name: string,
+ password: string,
+): Promise<WalletInfo> {
+ const path = `./tests/wallets/${name}`;
+ const cliPath = `./tests/${coin}-cli/${coin}-wallet-cli`;
+
+ await $
+ .raw`${cliPath} --generate-new-wallet ${path} --password ${password} --mnemonic-language English --command exit`
+ .stdout("null");
+
+ const address = (await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command address`
+ .stdinText(`${password}\n`)
+ .lines())
+ .at(-1)!
+ .split(/\s+/)[1];
+
+ const retrieveKeys = (lines: string[]) =>
+ lines.slice(-2)
+ .map((line) => line.split(": ")[1]);
+
+ const [secretSpendKey, publicSpendKey] = retrieveKeys(
+ await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command spendkey`
+ .stdinText(`${password}\n`)
+ .lines(),
+ );
+
+ const [secretViewKey, publicViewKey] = retrieveKeys(
+ await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command viewkey`
+ .stdinText(`${password}\n`)
+ .lines(),
+ );
+
+ return {
+ path,
+ password,
+ address,
+ secretSpendKey,
+ publicSpendKey,
+ secretViewKey,
+ publicViewKey,
+ };
+}
+
+// deno-lint-ignore ban-types
+export type MoneroCVersion = "next" | (string & {});
+
+export async function getMoneroCTags(): Promise<string[]> {
+ return ((
+ await (await fetch(
+ "https://api.github.com/repos/MrCyjanek/monero_c/releases",
+ )).json()
+ ) as { tag_name: string }[])
+ .map(({ tag_name }) => tag_name);
+}
+export async function getMoneroC(coin: Coin, version: MoneroCVersion) {
+ const dylibName = `${coin}_x86_64-linux-gnu_libwallet2_api_c.so`;
+ const endpointDylibName = `${coin}_libwallet2_api_c.so`;
+ const releaseDylibName = dylibName.slice(`${coin}_`.length);
+
+ if (version === "next") {
+ await $.raw`xz -kd release/${coin}/${releaseDylibName}.xz`;
+ await $`mkdir -p tests/libs/next`;
+ await $`mv release/${coin}/${releaseDylibName} tests/libs/next/${endpointDylibName}`;
+ } else {
+ const downloadUrl = `https://github.com/MrCyjaneK/monero_c/releases/download/${version}/${dylibName}.xz`;
+
+ const file = await Deno.open(`./tests/${dylibName}.xz`, {
+ create: true,
+ write: true,
+ });
+ file.write(await (await fetch(downloadUrl)).bytes());
+ file.close();
+
+ await $.raw`xz -d ./tests/${dylibName}.xz`;
+ await $.raw`mkdir -p ./tests/libs/${version}`;
+ await $
+ .raw`mv ./tests/${dylibName} ./tests/libs/${version}/${endpointDylibName}`;
+ }
+}