diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index adebed0a..37533b92 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,6 @@ FROM ghcr.io/cirruslabs/flutter:3.24.3 # FROM debian:bookworm +SHELL ["/bin/bash", "-c"] # Install dependencies @@ -15,11 +16,18 @@ RUN apt update \ RUN bash -c 'if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] then sudo rm -rf /opt/android-sdk-linux/platform-tools; fi' -# Install zsh and configure it + +ENV DENO_VERSION=v2.0.3 +RUN if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] then export DENO_ARCH=aarch64-unknown-linux-gnu; fi; \ + if [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "amd64" ]] then export DENO_ARCH=x86_64-unknown-linux-gnu; fi; \ + wget -O /tmp/deno.zip "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-${DENO_ARCH}.zip" && \ + cd /tmp && \ + unzip /tmp/deno.zip && \ + mv deno /usr/local/bin && \ + rm -rf deno deno.zip USER ubuntu +RUN git config --global --add safe.directory '*' RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.2.0/zsh-in-docker.sh)" \ -t robbyrussell - -RUN git config --global --add safe.directory '*' \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f6edac10..6342723a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,7 +17,8 @@ }, "extensions": [ "dart-code.flutter", - "dart-code.dart-code" + "dart-code.dart-code", + "denoland.vscode-deno" ] } }, diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index 19f2786b..2beda33b 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -609,7 +609,38 @@ jobs: path: release/${{ matrix.coin }} - name: Run regression tests - run: COIN="${{ matrix.coin }}" deno test -A tests/ + run: COIN="${{ matrix.coin }}" deno test -A tests/regression.test.ts + + integration_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 integration tests + run: COIN="${{ matrix.coin }}" deno test -A tests/integration.test.ts + env: + SECRET_WALLET_PASSWORD: ${{ secrets.SECRET_WALLET_PASSWORD }} + SECRET_WALLET_MNEMONIC: ${{ secrets.SECRET_WALLET_MNEMONIC }} + SECRET_WALLET_RESTORE_HEIGHT: ${{ secrets.SECRET_WALLET_RESTORE_HEIGHT }} + comment_pr: name: comment on pr diff --git a/.gitignore b/.gitignore index 2dbc30ee..9159f7de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ release/ build/ tests/monero-cli +tests/wownero-cli tests/libs tests/wallets diff --git a/impls/monero.ts/README.md b/impls/monero.ts/README.md index fe064670..9e3b27a1 100644 --- a/impls/monero.ts/README.md +++ b/impls/monero.ts/README.md @@ -23,7 +23,7 @@ There are at least two ways to do so: loadMoneroDylib(); const wm = await WalletManager.new(); - const wallet = await Wallet.create(wm, "./my_wallet", "password"); + const wallet = await wm.createWallet("./my_wallet", "password"); console.log(await wallet.address()); @@ -41,7 +41,7 @@ There are at least two ways to do so: loadMoneroDylib(lib); const wm = await WalletManager.new(); - const wallet = await Wallet.create(wm, "./my_wallet", "password"); + const wallet = await wm.createWallet("./my_wallet", "password"); console.log(await wallet.address()); diff --git a/impls/monero.ts/checksum.ts b/impls/monero.ts/checksum.ts index b0d12b02..81140d3a 100644 --- a/impls/monero.ts/checksum.ts +++ b/impls/monero.ts/checksum.ts @@ -1,6 +1,6 @@ import { moneroChecksum } from "./checksum_monero.ts"; -import { getSymbol, readCString } from "./src/utils.ts"; -import { dylib, loadMoneroDylib } from "./src/bindings.ts"; +import { readCString } from "./src/utils.ts"; +import { fns, loadMoneroDylib } from "./src/bindings.ts"; loadMoneroDylib(); @@ -21,7 +21,7 @@ export class ChecksumError extends Error { * @returns {ChecksumError} which contains information about why checksum failed */ export async function validateChecksum(): Promise { - const cppHeaderHash = await readCString(await getSymbol("checksum_wallet2_api_c_h")!(), false); + const cppHeaderHash = await readCString(await fns.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 { errorCode++; } - const cppSourceHash = await readCString(await getSymbol("checksum_wallet2_api_c_cpp")!(), false); + const cppSourceHash = await readCString(await fns.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 getSymbol("checksum_wallet2_api_c_exp")!(), false); + const cppExportHash = await readCString(await fns.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/mod.ts b/impls/monero.ts/mod.ts index 1eca7734..5b9af6c4 100644 --- a/impls/monero.ts/mod.ts +++ b/impls/monero.ts/mod.ts @@ -1,6 +1,11 @@ export * from "./src/bindings.ts"; +export * from "./src/coins.ts"; +export * from "./src/coins_info.ts"; export * from "./src/pending_transaction.ts"; +export * from "./src/symbols.ts"; export * from "./src/transaction_history.ts"; export * from "./src/transaction_info.ts"; +export * from "./src/unsigned_transaction.ts"; +export * from "./src/utils.ts"; export * from "./src/wallet.ts"; export * from "./src/wallet_manager.ts"; diff --git a/impls/monero.ts/src/bindings.ts b/impls/monero.ts/src/bindings.ts index 93ce8f9e..dc63c038 100644 --- a/impls/monero.ts/src/bindings.ts +++ b/impls/monero.ts/src/bindings.ts @@ -1,8 +1,21 @@ -import { type Dylib, moneroSymbols, type MoneroTsDylib, wowneroSymbols, type WowneroTsDylib } from "./symbols.ts"; +import { type MoneroSymbols, moneroSymbols, type SymbolName, type WowneroSymbols, wowneroSymbols } from "./symbols.ts"; + +export type MoneroDylib = Deno.DynamicLibrary; +export type WowneroDylib = Deno.DynamicLibrary; +export type Dylib = MoneroDylib | WowneroDylib; export let dylib: Dylib; -export function loadMoneroDylib(newDylib?: MoneroTsDylib) { +let dylibPrefix = "MONERO"; +export const fns = new Proxy({} as { [K in SymbolName]: MoneroDylib["symbols"][`MONERO_${K}`] }, { + get(_, symbolName: SymbolName) { + return dylib.symbols[`${dylibPrefix}_${symbolName}` as keyof Dylib["symbols"]]; + }, +}); + +export function loadMoneroDylib(newDylib?: MoneroDylib) { + dylibPrefix = "MONERO"; + if (newDylib) { dylib = newDylib; return; @@ -27,7 +40,9 @@ export function loadMoneroDylib(newDylib?: MoneroTsDylib) { dylib = Deno.dlopen(libPath, moneroSymbols); } -export function loadWowneroDylib(newDylib?: WowneroTsDylib) { +export function loadWowneroDylib(newDylib?: WowneroDylib) { + dylibPrefix = "WOWNERO"; + if (newDylib) { dylib = newDylib; return; diff --git a/impls/monero.ts/src/coins.ts b/impls/monero.ts/src/coins.ts new file mode 100644 index 00000000..45a222f8 --- /dev/null +++ b/impls/monero.ts/src/coins.ts @@ -0,0 +1,53 @@ +import { CoinsInfo, type CoinsInfoPtr } from "./coins_info.ts"; +import { fns } from "./bindings.ts"; + +export type CoinsPtr = Deno.PointerObject<"coins">; + +export class Coins { + #ptr: CoinsPtr; + + #coins: CoinsInfo[] = []; + + constructor(ptr: CoinsPtr) { + this.#ptr = ptr; + } + + async count(): Promise { + return await fns.Coins_count(this.#ptr); + } + + async coin(index: number): Promise { + if (this.#coins[index]) { + return this.#coins[index]; + } + + const coinPtr = await fns.Coins_coin(this.#ptr, index); + if (!coinPtr) return null; + + return CoinsInfo.new(coinPtr as CoinsInfoPtr); + } + + async setFrozen(index: number) { + return await fns.Coins_setFrozen(this.#ptr, index); + } + + async thaw(index: number) { + return await fns.Coins_thaw(this.#ptr, index); + } + + async getAllSize(): Promise { + return await fns.Coins_getAll_size(this.#ptr); + } + + async getAllByIndex(index: number): Promise { + return await fns.Coins_getAll_byIndex(this.#ptr, index); + } + + async refresh(): Promise { + await fns.Coins_refresh(this.#ptr); + + for (const coin of this.#coins) { + coin.refresh(); + } + } +} diff --git a/impls/monero.ts/src/coins_info.ts b/impls/monero.ts/src/coins_info.ts new file mode 100644 index 00000000..7ea18aa1 --- /dev/null +++ b/impls/monero.ts/src/coins_info.ts @@ -0,0 +1,85 @@ +import { fns } from "./bindings.ts"; +import { readCString } from "./utils.ts"; + +export type CoinsInfoPtr = Deno.PointerObject<"coinsInfo">; + +export class CoinsInfo { + #ptr: CoinsInfoPtr; + + #hash!: string | null; + #keyImage!: string | null; + #blockHeight!: bigint; + #amount!: bigint; + #spent!: boolean; + #spentHeight!: bigint; + #frozen!: boolean; + #unlocked!: boolean; + + constructor(ptr: CoinsInfoPtr) { + this.#ptr = ptr; + } + + getPointer(): CoinsInfoPtr { + return this.#ptr; + } + + static async new(ptr: CoinsInfoPtr): Promise { + const instance = new CoinsInfo(ptr); + await instance.refresh(); + return instance; + } + + async refresh() { + const [hash, keyImage, blockHeight, amount, spent, spentHeight, frozen, unlocked] = await Promise.all([ + fns.CoinsInfo_hash(this.#ptr).then(readCString), + fns.CoinsInfo_keyImage(this.#ptr).then(readCString), + fns.CoinsInfo_blockHeight(this.#ptr), + fns.CoinsInfo_amount(this.#ptr), + fns.CoinsInfo_spent(this.#ptr), + fns.CoinsInfo_spentHeight(this.#ptr), + fns.CoinsInfo_frozen(this.#ptr), + fns.CoinsInfo_unlocked(this.#ptr), + ]); + + this.#hash = hash; + this.#keyImage = keyImage; + this.#blockHeight = blockHeight; + this.#amount = amount; + this.#spent = spent; + this.#spentHeight = spentHeight; + this.#frozen = frozen; + this.#unlocked = unlocked; + } + + get hash(): string | null { + return this.#hash; + } + + get keyImage(): string | null { + return this.#keyImage; + } + + get blockHeight(): bigint { + return this.#blockHeight; + } + + get amount(): bigint { + return this.#amount; + } + + get spent(): boolean { + return this.#spent; + } + + get spentHeight(): bigint { + return this.#spentHeight; + } + + get frozen(): boolean { + return this.#frozen; + } + + get unlocked(): boolean { + return this.#unlocked; + } +} diff --git a/impls/monero.ts/src/pending_transaction.ts b/impls/monero.ts/src/pending_transaction.ts index 169332fd..d80b7389 100644 --- a/impls/monero.ts/src/pending_transaction.ts +++ b/impls/monero.ts/src/pending_transaction.ts @@ -1,87 +1,84 @@ -import { CString, getSymbol, readCString, type Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString } from "./utils.ts"; -export type PendingTransactionPtr = Deno.PointerObject<"transactionInfo">; +export type PendingTransactionPtr = Deno.PointerObject<"pendingTransaction">; -export class PendingTransaction { - #pendingTxPtr: PendingTransactionPtr; - sanitizer?: Sanitizer; +export class PendingTransaction { + #ptr: PendingTransactionPtr; - constructor(pendingTxPtr: PendingTransactionPtr, sanitizer?: Sanitizer) { - this.sanitizer = sanitizer; - this.#pendingTxPtr = pendingTxPtr; - } + #amount!: bigint; + #dust!: bigint; + #fee!: bigint; + #txid!: string | string[] | null; + #txCount!: bigint; - async status(): Promise { - return await getSymbol("PendingTransaction_status")(this.#pendingTxPtr); + constructor(ptr: PendingTransactionPtr) { + this.#ptr = ptr; } - async errorString(): Promise { - if (!await this.status()) return null; + static async new(ptr: PendingTransactionPtr): Promise { + const instance = new PendingTransaction(ptr); - const error = await getSymbol("PendingTransaction_errorString")(this.#pendingTxPtr); - if (!error) return null; + const [amount, dust, fee, txCount, txid] = await Promise.all([ + fns.PendingTransaction_amount(ptr), + fns.PendingTransaction_dust(ptr), + fns.PendingTransaction_fee(ptr), + fns.PendingTransaction_txCount(ptr), + fns.PendingTransaction_txid(ptr, C_SEPARATOR), + ]); - return await readCString(error) || null; - } + instance.#amount = amount; + instance.#dust = dust; + instance.#fee = fee; + instance.#txCount = txCount; + instance.#txid = maybeMultipleStrings(await readCString(txid)); - async throwIfError(sanitize = true): Promise { - const maybeError = await this.errorString(); - if (maybeError) { - if (sanitize) this.sanitizer?.(); - throw new Error(maybeError); - } + return instance; } - async commit(fileName: string, overwrite: boolean, sanitize = true): Promise { - const bool = await getSymbol("PendingTransaction_commit")( - this.#pendingTxPtr, - CString(fileName), - overwrite, - ); - await this.throwIfError(sanitize); - return bool; + get amount(): bigint { + return this.#amount; } - async commitUR(maxFragmentLength: number): Promise { - const commitUR = getSymbol("PendingTransaction_commitUR"); - - if (!commitUR) { - return null; - } + get dust(): bigint { + return this.#dust; + } - const result = await commitUR( - this.#pendingTxPtr, - maxFragmentLength, - ); + get fee(): bigint { + return this.#fee; + } - if (!result) return null; - await this.throwIfError(); - return await readCString(result) || null; + get txCount(): bigint { + return this.#txCount; } - async amount(): Promise { - return await getSymbol("PendingTransaction_amount")(this.#pendingTxPtr); + async commit(fileName: string, overwrite: boolean): Promise { + return await fns.PendingTransaction_commit(this.#ptr, CString(fileName), overwrite); } - async dust(): Promise { - return await getSymbol("PendingTransaction_dust")(this.#pendingTxPtr); + async commitUR(maxFragmentLength: number): Promise { + const commitUR = fns.PendingTransaction_commitUR; + if (!commitUR) return null; + + return await readCString( + await commitUR(this.#ptr, maxFragmentLength), + ); } - async fee(): Promise { - return await getSymbol("PendingTransaction_fee")(this.#pendingTxPtr); + async status(): Promise { + return await fns.PendingTransaction_status(this.#ptr); } - async txid(separator: string, sanitize = true): Promise { - const result = await getSymbol("PendingTransaction_txid")( - this.#pendingTxPtr, - CString(separator), - ); - if (!result) return null; - await this.throwIfError(sanitize); - return await readCString(result) || null; + async errorString(): Promise { + if (!await this.status()) return null; + const error = await fns.PendingTransaction_errorString(this.#ptr); + return await readCString(error); } - async txCount(): Promise { - return await getSymbol("PendingTransaction_txCount")(this.#pendingTxPtr); + async throwIfError(): Promise { + const maybeError = await this.errorString(); + if (maybeError) { + throw new Error(maybeError); + } } } diff --git a/impls/monero.ts/src/symbols.ts b/impls/monero.ts/src/symbols.ts index 38707b8f..b2cf965d 100644 --- a/impls/monero.ts/src/symbols.ts +++ b/impls/monero.ts/src/symbols.ts @@ -1,553 +1,2457 @@ export const moneroSymbols = { - "MONERO_WalletManagerFactory_getWalletManager": { + MONERO_PendingTransaction_status: { nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_commit: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + pendingTx_ptr: "pointer", + filename: "pointer", + overwrite: "bool", + ], + }, + MONERO_PendingTransaction_commitUR: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + pendingTx_ptr: "pointer", + max_fragment_length: "i32", + ], + }, + MONERO_PendingTransaction_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_dust: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_fee: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_txid: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_txCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_subaddrAccount: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_subaddrIndices: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_multisigSignData: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_signMultisigTx: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_signersKeys: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_hex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_txKey: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_status: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_amount: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_fee: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_mixin: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_confirmationMessage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_paymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_recipientAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_minMixinCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_txCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_sign: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + signedFileName: "pointer", + ], + }, + MONERO_UnsignedTransaction_signUR: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + unsignedTx_ptr: "pointer", + max_fragment_length: "i32", + ], + }, + MONERO_TransactionInfo_direction: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isPending: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isFailed: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isCoinbase: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_fee: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_blockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_description: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_subaddrIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + txInfo_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_TransactionInfo_subaddrAccount: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_label: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_confirmations: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_unlockTime: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_hash: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_timestamp: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_paymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_transfers_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_transfers_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "i32"] as [ + txInfo_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionInfo_transfers_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + txInfo_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionHistory_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txHistory_ptr: "pointer", + ], + }, + MONERO_TransactionHistory_transaction: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + txHistory_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionHistory_transactionById: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + txHistory_ptr: "pointer", + id: "pointer", + ], + }, + MONERO_TransactionHistory_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + txHistory_ptr: "pointer", + ], + }, + MONERO_TransactionHistory_setTxNote: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer"] as [ + txHistory_ptr: "pointer", + txid: "pointer", + note: "pointer", + ], + }, + MONERO_AddressBookRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getDescription: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getPaymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBook_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + addressBook_ptr: "pointer", + index: "i32", + ], + }, + MONERO_AddressBook_addRow: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + addressBook_ptr: "pointer", + dst_addr: "pointer", + payment_id: "pointer", + description: "pointer", + ], + }, + MONERO_AddressBook_deleteRow: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "usize"] as [ + addressBook_ptr: "pointer", + rowId: "usize", + ], + }, + MONERO_AddressBook_setDescription: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "usize", "pointer"] as [ + addressBook_ptr: "pointer", + rowId: "usize", + description: "pointer", + ], + }, + MONERO_AddressBook_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_errorCode: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_lookupPaymentID: { + nonblocking: true, + result: "i32", + parameters: ["pointer", "pointer"] as [ + addressBook_ptr: "pointer", + payment_id: "pointer", + ], + }, + MONERO_CoinsInfo_blockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_hash: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_internalOutputIndex: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_globalOutputIndex: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_spent: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_frozen: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_spentHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_rct: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_keyImageKnown: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_pkIndex: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_subaddrIndex: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_subaddrAccount: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_addressLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_keyImage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_unlockTime: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_unlocked: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_pubKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_coinbase: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_description: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_Coins_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_coin: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_setFrozenByPublicKey: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + ], + }, + MONERO_Coins_setFrozen: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_thaw: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_thawByPublicKey: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + ], + }, + MONERO_Coins_isTransferUnlocked: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "u64", "u64"] as [ + coins_ptr: "pointer", + unlockTime: "u64", + blockHeight: "u64", + ], + }, + MONERO_Coins_setDescription: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + description: "pointer", + ], + }, + MONERO_SubaddressRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_Subaddress_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + subaddress_ptr: "pointer", + ], + }, + MONERO_Subaddress_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + subaddress_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Subaddress_addRow: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_Subaddress_setLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32", "pointer"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + label: "pointer", + ], + }, + MONERO_Subaddress_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_SubaddressAccountRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getBalance: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getUnlockedBalance: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccount_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + subaddressAccount_ptr: "pointer", + ], + }, + MONERO_SubaddressAccount_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + subaddressAccount_ptr: "pointer", + index: "i32", + ], + }, + MONERO_SubaddressAccount_addRow: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + subaddressAccount_ptr: "pointer", + label: "pointer", + ], + }, + MONERO_SubaddressAccount_setLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + subaddressAccount_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_SubaddressAccount_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + subaddressAccount_ptr: "pointer", + ], + }, + MONERO_MultisigState_isMultisig: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_isReady: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_threshold: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_total: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_DeviceProgress_progress: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + deviceProgress_ptr: "pointer", + ], + }, + MONERO_DeviceProgress_indeterminate: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + deviceProgress_ptr: "pointer", + ], + }, + MONERO_Wallet_seed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + seed_offset: "pointer", + ], + }, + MONERO_Wallet_getSeedLanguage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setSeedLanguage: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + arg: "pointer", + ], + }, + MONERO_Wallet_status: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setPassword: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + password: "pointer", + ], + }, + MONERO_Wallet_getPassword: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setDevicePin: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + pin: "pointer", + ], + }, + MONERO_Wallet_setDevicePassphrase: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + passphrase: "pointer", + ], + }, + MONERO_Wallet_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "u64", "u64"] as [ + wallet_ptr: "pointer", + accountIndex: "u64", + addressIndex: "u64", + ], + }, + MONERO_Wallet_path: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_nettype: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_integratedAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + payment_id: "pointer", + ], + }, + MONERO_Wallet_secretViewKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicViewKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_secretSpendKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicSpendKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicMultisigSignerKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_stop: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_store: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + path: "pointer", + ], + }, + MONERO_Wallet_filename: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_keysFilename: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_init: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"] as [ + wallet_ptr: "pointer", + daemon_address: "pointer", + upper_transaction_size_limit: "u64", + daemon_username: "pointer", + daemon_password: "pointer", + use_ssl: "bool", + lightWallet: "bool", + proxy_address: "pointer", + ], + }, + MONERO_Wallet_createWatchOnly: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + ], + }, + MONERO_Wallet_setRefreshFromBlockHeight: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u64"] as [ + wallet_ptr: "pointer", + refresh_from_block_height: "u64", + ], + }, + MONERO_Wallet_getRefreshFromBlockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setRecoveringFromSeed: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + recoveringFromSeed: "bool", + ], + }, + MONERO_Wallet_setRecoveringFromDevice: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + recoveringFromDevice: "bool", + ], + }, + MONERO_Wallet_setSubaddressLookahead: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + major: "u32", + minor: "u32", + ], + }, + MONERO_Wallet_connectToDaemon: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_connected: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setTrustedDaemon: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + arg: "bool", + ], + }, + MONERO_Wallet_trustedDaemon: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setProxy: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_Wallet_balance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_unlockedBalance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_viewOnlyBalance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_watchOnly: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_isDeterministic: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_blockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_approximateBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_estimateBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_daemonBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_daemonBlockChainHeight_cached: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_daemonBlockChainHeight_runThread: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + wallet_ptr: "pointer", + seconds: "i32", + ], + }, + MONERO_Wallet_daemonBlockChainTargetHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_synchronized: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_displayAmount: { + nonblocking: true, + result: "pointer", + parameters: ["u64"] as [ + amount: "u64", + ], + }, + MONERO_Wallet_amountFromString: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + amount: "pointer", + ], + }, + MONERO_Wallet_amountFromDouble: { + nonblocking: true, + result: "u64", + parameters: ["f64"] as [ + amount: "f64", + ], + }, + MONERO_Wallet_genPaymentId: { + nonblocking: true, + result: "pointer", + parameters: [], + }, + MONERO_Wallet_paymentIdValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + paiment_id: "pointer", + ], + }, + MONERO_Wallet_addressValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "i32"] as [ + str: "pointer", + nettype: "i32", + ], + }, + MONERO_Wallet_keyValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool", "i32"] as [ + secret_key_string: "pointer", + address_string: "pointer", + isViewKey: "bool", + nettype: "i32", + ], + }, + MONERO_Wallet_keyValid_error: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "bool", "i32"] as [ + secret_key_string: "pointer", + address_string: "pointer", + isViewKey: "bool", + nettype: "i32", + ], + }, + MONERO_Wallet_paymentIdFromAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + strarg: "pointer", + nettype: "i32", + ], + }, + MONERO_Wallet_maximumAllowedAmount: { + nonblocking: true, + result: "u64", parameters: [], - // void* + }, + MONERO_Wallet_init3: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + argv0: "pointer", + default_log_base_name: "pointer", + log_path: "pointer", + console: "bool", + ], + }, + MONERO_Wallet_getPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + passphrase: "pointer", + ], + }, + MONERO_Wallet_createPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + language: "pointer", + ], + }, + MONERO_Wallet_startRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_pauseRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_refresh: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_refreshAsync: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_rescanBlockchain: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_rescanBlockchainAsync: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setAutoRefreshInterval: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + wallet_ptr: "pointer", + millis: "i32", + ], + }, + MONERO_Wallet_autoRefreshInterval: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_addSubaddressAccount: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + label: "pointer", + ], + }, + MONERO_Wallet_numSubaddressAccounts: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_numSubaddresses: { + nonblocking: true, + result: "usize", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_addSubaddress: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_Wallet_getSubaddressLabel: { + nonblocking: true, result: "pointer", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + ], }, - - //#region WalletManager - "MONERO_WalletManager_createWallet": { + MONERO_Wallet_setSubaddressLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32", "pointer"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + label: "pointer", + ], + }, + MONERO_Wallet_multisig: { + optional: true, 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", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_openWallet": { + MONERO_Wallet_getMultisigInfo: { nonblocking: true, - // void* wm_ptr, const char* path, const char* password, int networkType - "parameters": ["pointer", "pointer", "pointer", "i32"], - // void* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_recoveryWallet": { + MONERO_Wallet_makeMultisig: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "u32"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + threshold: "u32", + ], + }, + MONERO_Wallet_exchangeMultisigKeys: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + force_update_use_with_caution: "bool", + ], + }, + MONERO_Wallet_exportMultisigImages: { + optional: true, 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", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + separator: "pointer", + ], }, - "MONERO_WalletManager_blockchainHeight": { + MONERO_Wallet_importMultisigImages: { + optional: true, nonblocking: true, - // void* wm_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + ], }, - "MONERO_WalletManager_blockchainTargetHeight": { + MONERO_Wallet_hasMultisigPartialKeyImages: { + optional: true, nonblocking: true, - // void* wm_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_setDaemonAddress": { + MONERO_Wallet_restoreMultisigTransaction: { + optional: true, nonblocking: true, - // void* wm_ptr, const char* address - parameters: ["pointer", "pointer"], - // void - result: "void", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + signData: "pointer", + ], }, - //#endregion - - //#region Wallet - "MONERO_Wallet_init": { + MONERO_Wallet_createTransactionMultDest: { 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", + result: "pointer", + parameters: [ + "pointer", + "pointer", + "pointer", + "pointer", + "bool", + "pointer", + "pointer", + "u32", + "i32", + "u32", + "pointer", + "pointer", + ] as [ + wallet_ptr: "pointer", + dst_addr_list: "pointer", + dst_addr_list_separator: "pointer", + payment_id: "pointer", + amount_sweep_all: "bool", + amount_list: "pointer", + amount_list_separator: "pointer", + mixin_count: "u32", + pendingTransactionPriority: "i32", + subaddr_account: "u32", + preferredInputs: "pointer", + preferredInputs_separator: "pointer", + ], + }, + MONERO_Wallet_createTransaction: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + dst_addr: "pointer", + payment_id: "pointer", + amount: "u64", + mixin_count: "u32", + pendingTransactionPriority: "i32", + subaddr_account: "u32", + preferredInputs: "pointer", + separator: "pointer", + ], + }, + MONERO_Wallet_loadUnsignedTx: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + fileName: "pointer", + ], }, - "MONERO_Wallet_init3": { + MONERO_Wallet_loadUnsignedTxUR: { + optional: true, 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", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_setTrustedDaemon": { + MONERO_Wallet_submitTransaction: { nonblocking: true, - // void* wallet_ptr, bool arg - parameters: ["pointer", "bool"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + fileName: "pointer", + ], }, - "MONERO_Wallet_startRefresh": { + MONERO_Wallet_submitTransactionUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_refreshAsync": { + MONERO_Wallet_hasUnknownKeyImages: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_blockChainHeight": { + MONERO_Wallet_exportKeyImages: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + filename: "pointer", + all: "bool", + ], }, - "MONERO_Wallet_daemonBlockChainHeight": { + MONERO_Wallet_exportKeyImagesUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "pointer", + parameters: ["pointer", "usize", "bool"] as [ + wallet_ptr: "pointer", + max_fragment_length: "usize", + all: "bool", + ], + }, + MONERO_Wallet_importKeyImages: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + filename: "pointer", + ], }, - "MONERO_Wallet_synchronized": { + MONERO_Wallet_importKeyImagesUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_store": { + MONERO_Wallet_exportOutputs: { nonblocking: true, - // void* wallet_ptr, const char* path - parameters: ["pointer", "pointer"], - // bool result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + filename: "pointer", + all: "bool", + ], }, - "MONERO_Wallet_address": { + MONERO_Wallet_exportOutputsUR: { + optional: true, nonblocking: true, - // void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex - parameters: ["pointer", "u64", "u64"], - // char* result: "pointer", + parameters: ["pointer", "usize", "bool"] as [ + wallet_ptr: "pointer", + max_fragment_length: "usize", + all: "bool", + ], }, - "MONERO_Wallet_balance": { + MONERO_Wallet_importOutputs: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + filename: "pointer", + ], }, - "MONERO_Wallet_unlockedBalance": { + MONERO_Wallet_importOutputsUR: { + optional: true, nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_addSubaddressAccount": { + MONERO_Wallet_setupBackgroundSync: { nonblocking: true, - // void* wallet_ptr, const char* label - parameters: ["pointer", "pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "i32", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + background_sync_type: "i32", + wallet_password: "pointer", + background_cache_password: "pointer", + ], }, - "MONERO_Wallet_numSubaddressAccounts": { + MONERO_Wallet_getBackgroundSyncType: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // size_t - result: "usize", + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_addSubaddress": { + MONERO_Wallet_startBackgroundSync: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, const char* label - parameters: ["pointer", "u32", "pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_numSubaddresses": { + MONERO_Wallet_stopBackgroundSync: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // size_t - result: "usize", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + wallet_password: "pointer", + ], + }, + MONERO_Wallet_isBackgroundSyncing: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_getSubaddressLabel": { + MONERO_Wallet_isBackgroundWallet: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_history: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex - parameters: ["pointer", "u32", "u32"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_setSubaddressLabel": { + MONERO_Wallet_addressBook: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label - parameters: ["pointer", "u32", "u32", "pointer"], - // void - result: "void", + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_status": { + MONERO_Wallet_coins: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_errorString": { + MONERO_Wallet_subaddress: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_history": { + MONERO_Wallet_subaddressAccount: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_defaultMixin: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setDefaultMixin: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + arg: "u32", + ], + }, + MONERO_Wallet_setCacheAttribute: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + key: "pointer", + val: "pointer", + ], }, - "MONERO_Wallet_createTransaction": { + MONERO_Wallet_getCacheAttribute: { 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", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + key: "pointer", + ], }, - "MONERO_Wallet_amountFromString": { + MONERO_Wallet_setUserNote: { nonblocking: true, - // const char* amount - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + note: "pointer", + ], }, - //#endregion - - //#region TransactionHistory - "MONERO_TransactionHistory_count": { + MONERO_Wallet_getUserNote: { nonblocking: true, - // void* txHistory_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + ], }, - "MONERO_TransactionHistory_transaction": { + MONERO_Wallet_getTxKey: { nonblocking: true, - // void* txHistory_ptr, int index - parameters: ["pointer", "i32"], - // void* result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + ], }, - "MONERO_TransactionHistory_transactionById": { + MONERO_Wallet_signMessage: { nonblocking: true, - // void* txHistory_ptr, const char* id - parameters: ["pointer", "pointer"], - // void* result: "pointer", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + message: "pointer", + address: "pointer", + ], + }, + MONERO_Wallet_verifySignedMessage: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + message: "pointer", + address: "pointer", + signature: "pointer", + ], }, - "MONERO_TransactionHistory_refresh": { + MONERO_Wallet_rescanSpent: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setOffline: { nonblocking: true, - // void* txHistory_ptr - parameters: ["pointer"], - // void result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + offline: "bool", + ], + }, + MONERO_Wallet_isOffline: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionHistory_setTxNote": { + MONERO_Wallet_segregatePreForkOutputs: { nonblocking: true, - // void* txHistory_ptr, const char* txid, const char* note - parameters: ["pointer", "pointer", "pointer"], - // void result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + segregate: "bool", + ], }, - //#endregion - - //#region TransactionInfo - "MONERO_TransactionInfo_direction": { + MONERO_Wallet_segregationHeight: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "void", + parameters: ["pointer", "u64"] as [ + wallet_ptr: "pointer", + height: "u64", + ], + }, + MONERO_Wallet_keyReuseMitigation2: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + mitigation: "bool", + ], }, - "MONERO_TransactionInfo_isPending": { + MONERO_Wallet_lockKeysFile: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_isFailed": { + MONERO_Wallet_unlockKeysFile: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_isCoinbase": { + MONERO_Wallet_isKeysFileLocked: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_amount": { + MONERO_Wallet_getDeviceType: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_coldKeyImageSync: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer", "u64", "u64"] as [ + wallet_ptr: "pointer", + spent: "u64", + unspent: "u64", + ], + }, + MONERO_Wallet_deviceShowAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + ], + }, + MONERO_Wallet_reconnectDevice: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_fee": { + MONERO_Wallet_getBytesReceived: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_blockHeight": { + MONERO_Wallet_getBytesSent: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_description": { + MONERO_Wallet_getStateIsConnected: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_subaddrIndex": { + MONERO_Wallet_getSendToDevice: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_subaddrAccount": { + MONERO_Wallet_getSendToDeviceLength: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint32_t - result: "u32", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_label": { + MONERO_Wallet_getReceivedFromDevice: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_confirmations": { + MONERO_Wallet_getReceivedFromDeviceLength: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_unlockTime": { + MONERO_Wallet_getWaitsForDeviceSend: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_hash": { + MONERO_Wallet_getWaitsForDeviceReceive: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_timestamp": { + MONERO_Wallet_setDeviceReceivedData: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "void", + parameters: ["pointer", "pointer", "usize"] as [ + wallet_ptr: "pointer", + data: "pointer", + len: "usize", + ], }, - "MONERO_TransactionInfo_paymentId": { + MONERO_Wallet_setDeviceSendData: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "void", + parameters: ["pointer", "pointer", "usize"] as [ + wallet_ptr: "pointer", + data: "pointer", + len: "usize", + ], }, - "MONERO_TransactionInfo_transfers_count": { + MONERO_WalletManager_createWallet: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + networkType: "i32", + ], }, - "MONERO_TransactionInfo_transfers_amount": { + MONERO_WalletManager_openWallet: { nonblocking: true, - // void* txInfo_ptr, int index - parameters: ["pointer", "i32"], - // uint64_t - result: "u64", + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + networkType: "i32", + ], }, - "MONERO_TransactionInfo_transfers_address": { + MONERO_WalletManager_recoveryWallet: { nonblocking: true, - // void* txInfo_ptr, int index - parameters: ["pointer", "i32"], - // const char* result: "pointer", - }, - //#endregion - - //#region PendingTransaction - "MONERO_PendingTransaction_status": { + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + mnemonic: "pointer", + networkType: "i32", + restoreHeight: "u64", + kdfRounds: "u64", + seedOffset: "pointer", + ], + }, + MONERO_WalletManager_createWalletFromKeys: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // int - result: "i32", - }, - "MONERO_PendingTransaction_errorString": { + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + nettype: "i32", + restoreHeight: "u64", + addressString: "pointer", + viewKeyString: "pointer", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createWalletFromDevice: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32", "pointer", "u64", "pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + nettype: "i32", + deviceName: "pointer", + restoreHeight: "u64", + subaddressLookahead: "pointer", + viewKeyString: "pointer", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createDeterministicWalletFromSpendKey: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + nettype: "i32", + restoreHeight: "u64", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createWalletFromPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32", "pointer", "pointer", "bool", "u64", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + nettype: "i32", + mnemonic: "pointer", + passphrase: "pointer", + newWallet: "bool", + restore_height: "u64", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_closeWallet: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wm_ptr: "pointer", + wallet_ptr: "pointer", + store: "bool", + ], }, - "MONERO_PendingTransaction_commit": { + MONERO_WalletManager_walletExists: { nonblocking: true, - // void* pendingTx_ptr, const char* filename, bool overwrite - parameters: ["pointer", "pointer", "bool"], - // bool result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + ], }, - "MONERO_PendingTransaction_commitUR": { + MONERO_WalletManager_verifyWalletPassword: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "bool", "u64"] as [ + wm_ptr: "pointer", + keys_file_name: "pointer", + password: "pointer", + no_spend_key: "bool", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_queryWalletDevice: { optional: true, nonblocking: true, - // void* pendingTx_ptr, int max_fragment_length - parameters: ["pointer", "i32"], - // const char* + result: "i32", + parameters: ["pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + keys_file_name: "pointer", + password: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_findWallets: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + separator: "pointer", + ], + }, + MONERO_WalletManager_errorString: { + nonblocking: true, result: "pointer", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], + }, + MONERO_WalletManager_setDaemonAddress: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_WalletManager_setProxy: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], }, - "MONERO_PendingTransaction_amount": { + MONERO_WalletManager_blockchainHeight: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_dust": { + MONERO_WalletManager_blockchainTargetHeight: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_fee": { + MONERO_WalletManager_networkDifficulty: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_txid": { + MONERO_WalletManager_miningHashRate: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* - result: "pointer", + result: "f64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_txCount": { + MONERO_WalletManager_blockTarget: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_subaddrAccount": { + MONERO_WalletManager_isMining: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], + }, + MONERO_WalletManager_startMining: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "u32", "bool", "bool"] as [ + wm_ptr: "pointer", + address: "pointer", + threads: "u32", + backgroundMining: "bool", + ignoreBattery: "bool", + ], }, - "MONERO_PendingTransaction_subaddrIndices": { + MONERO_WalletManager_stopMining: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_WalletManager_resolveOpenAlias: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* result: "pointer", + parameters: ["pointer", "pointer", "bool"] as [ + wm_ptr: "pointer", + address: "pointer", + dnssec_valid: "bool", + ], }, - "MONERO_PendingTransaction_multisigSignData": { + MONERO_WalletManagerFactory_getWalletManager: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: [], + }, + MONERO_WalletManagerFactory_setLogLevel: { + nonblocking: true, + result: "void", + parameters: ["i32"] as [ + level: "i32", + ], + }, + MONERO_WalletManagerFactory_setLogCategories: { + optional: true, + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + categories: "pointer", + ], }, - "MONERO_PendingTransaction_signMultisigTx": { + MONERO_DEBUG_test0: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // void result: "void", + parameters: [], + }, + MONERO_DEBUG_test1: { + nonblocking: true, + result: "bool", + parameters: ["bool"] as [ + x: "bool", + ], + }, + MONERO_DEBUG_test2: { + nonblocking: true, + result: "i32", + parameters: ["i32"] as [ + x: "i32", + ], + }, + MONERO_DEBUG_test3: { + nonblocking: true, + result: "u64", + parameters: ["u64"] as [ + x: "u64", + ], }, - "MONERO_PendingTransaction_signersKeys": { + MONERO_DEBUG_test4: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["u64"] as [ + x: "u64", + ], }, - "MONERO_PendingTransaction_hex": { + MONERO_DEBUG_test5: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* result: "pointer", + parameters: [], }, - //#endregion - - //#region Checksum - "MONERO_checksum_wallet2_api_c_h": { - optional: true, + MONERO_DEBUG_test5_std: { nonblocking: true, + result: "pointer", parameters: [], - // const char* + }, + MONERO_DEBUG_isPointerNull: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_cw_getWalletListener: { + nonblocking: true, result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_resetNeedToRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_isNeedToRefresh: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_isNewTransactionExist: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_resetIsNewTransactionExist: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], }, - "MONERO_checksum_wallet2_api_c_cpp": { + MONERO_cw_WalletListener_height: { + optional: true, + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_checksum_wallet2_api_c_h: { optional: true, nonblocking: true, - parameters: [], - // const char* result: "pointer", + parameters: [], }, - "MONERO_checksum_wallet2_api_c_exp": { + MONERO_checksum_wallet2_api_c_cpp: { optional: true, nonblocking: true, + result: "pointer", parameters: [], - // const char* + }, + MONERO_checksum_wallet2_api_c_exp: { + optional: true, + nonblocking: true, result: "pointer", + parameters: [], }, - //#endregion - - "MONERO_free": { + MONERO_free: { nonblocking: true, - // void* ptr - parameters: ["pointer"], - // void result: "void", + parameters: ["pointer"] as [ + ptr: "pointer", + ], }, } as const; -type MoneroSymbols = typeof moneroSymbols; -export type MoneroTsDylib = Deno.DynamicLibrary; +export type MoneroSymbols = typeof moneroSymbols; type ReplaceMonero = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never; +export type WowneroSymbols = { [Key in keyof MoneroSymbols as ReplaceMonero]: MoneroSymbols[Key] }; -type WowneroSymbols = { [Key in keyof MoneroSymbols as ReplaceMonero]: MoneroSymbols[Key] }; +export type SymbolName = keyof MoneroSymbols extends `MONERO_${infer SymbolName}` ? SymbolName : never; export const wowneroSymbols = Object.fromEntries( Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]), ) as WowneroSymbols; - -export type WowneroTsDylib = Deno.DynamicLibrary; - -export type Dylib = MoneroTsDylib | WowneroTsDylib; diff --git a/impls/monero.ts/src/transaction_history.ts b/impls/monero.ts/src/transaction_history.ts index aab64da7..cec3fdec 100644 --- a/impls/monero.ts/src/transaction_history.ts +++ b/impls/monero.ts/src/transaction_history.ts @@ -1,34 +1,41 @@ +import { fns } from "./bindings.ts"; import { TransactionInfo, TransactionInfoPtr } from "./transaction_info.ts"; -import { CString, getSymbol } from "./utils.ts"; +import { CString } from "./utils.ts"; export type TransactionHistoryPtr = Deno.PointerObject<"transactionHistory">; export class TransactionHistory { - #txHistoryPtr: TransactionHistoryPtr; + #ptr: TransactionHistoryPtr; - constructor(txHistoryPtr: TransactionHistoryPtr) { - this.#txHistoryPtr = txHistoryPtr; + #count!: number; + + constructor(ptr: TransactionHistoryPtr) { + this.#ptr = ptr; + } + + static async new(ptr: TransactionHistoryPtr) { + const instance = new TransactionHistory(ptr); + instance.#count = await fns.TransactionHistory_count(ptr); + return instance; } - async count(): Promise { - return await getSymbol("TransactionHistory_count")(this.#txHistoryPtr); + get count(): number { + return this.#count; } async transaction(index: number): Promise { - return new TransactionInfo( - ( - await getSymbol("TransactionHistory_transaction")(this.#txHistoryPtr, index) - ) as TransactionInfoPtr, + return TransactionInfo.new( + await fns.TransactionHistory_transaction(this.#ptr, index) as TransactionInfoPtr, ); } async refresh(): Promise { - await getSymbol("TransactionHistory_refresh")(this.#txHistoryPtr); + await fns.TransactionHistory_refresh(this.#ptr); } async setTxNote(transactionId: string, note: string): Promise { - await getSymbol("TransactionHistory_setTxNote")( - this.#txHistoryPtr, + await fns.TransactionHistory_setTxNote( + this.#ptr, CString(transactionId), CString(note), ); diff --git a/impls/monero.ts/src/transaction_info.ts b/impls/monero.ts/src/transaction_info.ts index 22ea0e78..9becfb99 100644 --- a/impls/monero.ts/src/transaction_info.ts +++ b/impls/monero.ts/src/transaction_info.ts @@ -1,104 +1,147 @@ -import { dylib } from "./bindings.ts"; -import { getSymbol, readCString, Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString, SEPARATOR } from "./utils.ts"; -export type TransactionInfoPtr = Deno.PointerObject<"transactionInfo">; +export type TransactionInfoPtr = Deno.PointerObject<"pendingTransaction">; -export class TransactionInfo { - #txInfoPtr: TransactionInfoPtr; - sanitizer?: Sanitizer; - - constructor(txInfoPtr: TransactionInfoPtr, sanitizer?: Sanitizer) { - this.#txInfoPtr = txInfoPtr; - this.sanitizer = sanitizer; - } +export interface TransferData { + address: string | null; + amount: bigint; +} - async direction(): Promise<"in" | "out"> { - switch (await getSymbol("TransactionInfo_direction")(this.#txInfoPtr)) { - case 0: - return "in"; - case 1: - return "out"; - default: - await this.sanitizer?.(); - throw new Error("Invalid TransactionInfo direction"); +export class TransactionInfo { + #ptr: TransactionInfoPtr; + + #amount!: bigint; + #fee!: bigint; + #timestamp!: bigint; + #transfersCount!: number; + #paymentId!: string | null; + #hash!: string | null; + + #subaddrAccount!: number; + #subaddrIndex!: string | null; + + #transfers!: readonly TransferData[]; + + constructor(ptr: TransactionInfoPtr) { + this.#ptr = ptr; + } + + static async new(ptr: TransactionInfoPtr): Promise { + const instance = new TransactionInfo(ptr); + + const [amount, paymentId, fee, hash, subaddrIndex, subaddrAccount, timestamp, transfersCount] = await Promise.all([ + fns.TransactionInfo_amount(ptr), + fns.TransactionInfo_paymentId(ptr).then(readCString), + fns.TransactionInfo_fee(ptr), + fns.TransactionInfo_hash(ptr).then(readCString), + fns.TransactionInfo_subaddrIndex(ptr, C_SEPARATOR).then(readCString), + fns.TransactionInfo_subaddrAccount(ptr), + fns.TransactionInfo_timestamp(ptr), + fns.TransactionInfo_transfers_count(ptr), + ]); + + instance.#amount = amount; + instance.#fee = fee; + instance.#timestamp = timestamp; + instance.#transfersCount = transfersCount; + instance.#paymentId = paymentId; + instance.#hash = hash; + + instance.#subaddrAccount = subaddrAccount; + instance.#subaddrIndex = subaddrIndex; + + const transfers = []; + for (let i = 0; i < transfersCount; ++i) { + const [amount, address] = await Promise.all([ + fns.TransactionInfo_transfers_amount(ptr, i), + fns.TransactionInfo_transfers_address(ptr, i).then(readCString), + ]); + + transfers.push({ amount, address }); } - } + Object.freeze(transfers); + instance.#transfers = transfers; - async isPending(): Promise { - return await getSymbol("TransactionInfo_isPending")(this.#txInfoPtr); + return instance; } - async isFailed(): Promise { - return await getSymbol("TransactionInfo_isFailed")(this.#txInfoPtr); + get amount(): bigint { + return this.#amount; } - async isCoinbase(): Promise { - return await getSymbol("TransactionInfo_isCoinbase")(this.#txInfoPtr); + get fee(): bigint { + return this.#fee; } - async amount(): Promise { - return await getSymbol("TransactionInfo_amount")(this.#txInfoPtr); + get timestamp(): bigint { + return this.#timestamp; } - async fee(): Promise { - return await getSymbol("TransactionInfo_fee")(this.#txInfoPtr); + get transfersCount(): number { + return this.#transfersCount; } - async blockHeight(): Promise { - return await getSymbol("TransactionInfo_blockHeight")(this.#txInfoPtr); + get paymentId(): string | null { + return this.#paymentId; } - async description(): Promise { - const description = await getSymbol("TransactionInfo_description")(this.#txInfoPtr); - return await readCString(description) || ""; + get hash(): string | null { + return this.#hash; } - async subaddrIndex(): Promise { - const subaddrIndex = await getSymbol("TransactionInfo_subaddrIndex")(this.#txInfoPtr); - return await readCString(subaddrIndex) || ""; + get subaddrAccount(): number { + return this.#subaddrAccount; } - async subaddrAccount(): Promise { - return await getSymbol("TransactionInfo_subaddrAccount")(this.#txInfoPtr); + get subaddrIndex(): string | null { + return this.#subaddrIndex; } - async label(): Promise { - const label = await getSymbol("TransactionInfo_label")(this.#txInfoPtr); - return await readCString(label) || ""; + get transfers(): readonly TransferData[] { + return this.#transfers; } - async confirmations(): Promise { - return await getSymbol("TransactionInfo_confirmations")(this.#txInfoPtr); + async direction(): Promise<"in" | "out"> { + switch (await fns.TransactionInfo_direction(this.#ptr)) { + case 0: + return "in"; + case 1: + return "out"; + default: + throw new Error("Invalid TransactionInfo direction"); + } } - async unlockTime(): Promise { - return await getSymbol("TransactionInfo_unlockTime")(this.#txInfoPtr); + async description(): Promise { + return await readCString( + await fns.TransactionInfo_description(this.#ptr), + ); } - async hash(): Promise { - const hash = await getSymbol("TransactionInfo_hash")(this.#txInfoPtr); - return await readCString(hash) || ""; + async label(): Promise { + return await readCString( + await fns.TransactionInfo_label(this.#ptr), + ); } - async timestamp(): Promise { - return await getSymbol("TransactionInfo_timestamp")(this.#txInfoPtr); + async confirmations(): Promise { + return await fns.TransactionInfo_confirmations(this.#ptr); } - async paymentId(): Promise { - const paymentId = await getSymbol("TransactionInfo_paymentId")(this.#txInfoPtr); - return await readCString(paymentId) || ""; + async unlockTime(): Promise { + return await fns.TransactionInfo_unlockTime(this.#ptr); } - async transfersCount(): Promise { - return await getSymbol("TransactionInfo_transfers_count")(this.#txInfoPtr); + async isPending(): Promise { + return await fns.TransactionInfo_isPending(this.#ptr); } - async transfersAmount(index: number): Promise { - return await getSymbol("TransactionInfo_transfers_amount")(this.#txInfoPtr, index); + async isFailed(): Promise { + return await fns.TransactionInfo_isFailed(this.#ptr); } - async transfersAddress(index: number): Promise { - const transfersAddress = await getSymbol("TransactionInfo_transfers_address")(this.#txInfoPtr, index); - return await readCString(transfersAddress) || ""; + async isCoinbase(): Promise { + return await fns.TransactionInfo_isCoinbase(this.#ptr); } } diff --git a/impls/monero.ts/src/unsigned_transaction.ts b/impls/monero.ts/src/unsigned_transaction.ts new file mode 100644 index 00000000..c5d8ed7b --- /dev/null +++ b/impls/monero.ts/src/unsigned_transaction.ts @@ -0,0 +1,79 @@ +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString } from "./utils.ts"; + +export type UnsignedTransactionPtr = Deno.PointerObject<"pendingTransaction">; + +export class UnsignedTransaction { + #ptr: UnsignedTransactionPtr; + + #amount!: string | string[] | null; + #fee!: string | string[] | null; + #txCount!: bigint; + #paymentId!: string | null; + #recipientAddress!: string | string[] | null; + + constructor(ptr: UnsignedTransactionPtr) { + this.#ptr = ptr; + } + + async status(): Promise { + return await fns.UnsignedTransaction_status(this.#ptr); + } + + async errorString(): Promise { + return await readCString(await fns.UnsignedTransaction_errorString(this.#ptr)); + } + + static async new(ptr: UnsignedTransactionPtr): Promise { + const instance = new UnsignedTransaction(ptr); + + const [amount, paymentId, fee, txCount, recipientAddress] = await Promise.all([ + fns.UnsignedTransaction_amount(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_paymentId(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_fee(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_txCount(ptr), + fns.UnsignedTransaction_recipientAddress(ptr, C_SEPARATOR).then(readCString), + ]); + + instance.#amount = maybeMultipleStrings(amount); + instance.#fee = maybeMultipleStrings(fee); + instance.#recipientAddress = maybeMultipleStrings(recipientAddress); + instance.#txCount = txCount; + instance.#paymentId = paymentId; + + return instance; + } + + get amount(): string | string[] | null { + return this.#amount; + } + + get fee(): string | string[] | null { + return this.#fee; + } + + get txCount(): bigint { + return this.#txCount; + } + + get paymentId(): string | null { + return this.#paymentId; + } + + get recipientAddress(): string | string[] | null { + return this.#recipientAddress; + } + + async sign(signedFileName: string): Promise { + return await fns.UnsignedTransaction_sign(this.#ptr, CString(signedFileName)); + } + + async signUR(maxFragmentLength: number): Promise { + const signUR = fns.UnsignedTransaction_signUR; + if (!signUR) return null; + + return await readCString( + await signUR(this.#ptr, maxFragmentLength), + ); + } +} diff --git a/impls/monero.ts/src/utils.ts b/impls/monero.ts/src/utils.ts index e88ddcd1..4323b72a 100644 --- a/impls/monero.ts/src/utils.ts +++ b/impls/monero.ts/src/utils.ts @@ -1,22 +1,19 @@ -import { dylib } from "../mod.ts"; -import type { moneroSymbols, MoneroTsDylib, WowneroTsDylib } from "./symbols.ts"; - -export type Sanitizer = () => void | PromiseLike; +import { fns } from "./bindings.ts"; const textEncoder = new TextEncoder(); -export function CString(string: string): Deno.PointerValue { - return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`)); +export const SEPARATOR = ","; +export const C_SEPARATOR = CString(SEPARATOR); + +export function maybeMultipleStrings(input: string): string | string[]; +export function maybeMultipleStrings(input: null | string): null | string | string[]; +export function maybeMultipleStrings(input: null | string): null | string | string[] { + if (!input) return null; + const multiple = input.split(SEPARATOR); + return multiple.length === 1 ? multiple[0] : multiple; } -type SymbolWithoutPrefix = keyof typeof moneroSymbols extends `MONERO_${infer DylibSymbol}` ? DylibSymbol : never; -export function getSymbol( - 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]; - } +export function CString(string: string): Deno.PointerValue { + return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`)); } /** @@ -29,9 +26,8 @@ export async function readCString(pointer: Deno.PointerObject, free?: boolean): export async function readCString(pointer: Deno.PointerValue, free?: boolean): Promise; export async function readCString(pointer: Deno.PointerValue, free = true): Promise { if (!pointer) return null; + const string = new Deno.UnsafePointerView(pointer).getCString(); - if (free) { - await getSymbol("free")(pointer); - } + if (string && free) await fns.free(pointer); return string; } diff --git a/impls/monero.ts/src/wallet.ts b/impls/monero.ts/src/wallet.ts index ea25f21f..673ccabf 100644 --- a/impls/monero.ts/src/wallet.ts +++ b/impls/monero.ts/src/wallet.ts @@ -1,294 +1,268 @@ -import { dylib } from "./bindings.ts"; -import { CString, getSymbol, readCString, Sanitizer } from "./utils.ts"; +import { WalletManager } from "./wallet_manager.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"; +import { C_SEPARATOR, CString, readCString, SEPARATOR } from "./utils.ts"; +import { PendingTransaction, PendingTransactionPtr } from "./pending_transaction.ts"; +import { UnsignedTransaction, UnsignedTransactionPtr } from "./unsigned_transaction.ts"; +import { Coins, CoinsPtr } from "./coins.ts"; +import { fns } from "./bindings.ts"; export type WalletPtr = Deno.PointerObject<"walletManager">; +interface DaemonInfo { + address?: string; + username?: string; + password?: string; + lightWallet?: boolean; + proxyAddress?: string; +} + export class Wallet { - #walletManagerPtr: WalletManagerPtr; - #walletPtr: WalletPtr; - sanitizer?: Sanitizer; + #walletManager: WalletManager; + #ptr: WalletPtr; - constructor(walletManagerPtr: WalletManager, walletPtr: WalletPtr, sanitizer?: Sanitizer) { - this.#walletPtr = walletPtr; - this.#walletManagerPtr = walletManagerPtr.getPointer(); - this.sanitizer = sanitizer; + constructor(walletManager: WalletManager, ptr: WalletPtr) { + this.#walletManager = walletManager; + this.#ptr = ptr; } - getPointer(): WalletPtr { - return this.#walletPtr; + getPointer() { + return this.#ptr; } - async store(path = ""): Promise { - const bool = await getSymbol("Wallet_store")(this.#walletPtr, CString(path)); - await this.throwIfError(); - return bool; - } + async init(daemonInfo: DaemonInfo, log = false): Promise { + const success = await fns.Wallet_init( + this.#ptr, + CString(daemonInfo.address ?? ""), + 0n, + CString(daemonInfo.username ?? ""), + CString(daemonInfo.password ?? ""), + false, + daemonInfo.lightWallet ?? false, + CString(daemonInfo.proxyAddress ?? ""), + ); + + if (log) { + await fns.Wallet_init3( + this.#ptr, + CString(""), + CString(""), + CString(""), + true, + ); + } - async initWallet(daemonAddress = "http://nodex.monerujo.io:18081"): Promise { - await this.init(); await this.setTrustedDaemon(true); - await this.setDaemonAddress(daemonAddress); await this.startRefresh(); await this.refreshAsync(); - await this.throwIfError(); + + return success; } - async setDaemonAddress(address: string): Promise { - await getSymbol("WalletManager_setDaemonAddress")( - this.#walletManagerPtr, - CString(address), - ); + async setTrustedDaemon(value: boolean): Promise { + return await fns.Wallet_setTrustedDaemon(this.#ptr, value); } async startRefresh(): Promise { - await getSymbol("Wallet_startRefresh")(this.#walletPtr); - await this.throwIfError(); + return await fns.Wallet_startRefresh(this.#ptr); } async refreshAsync(): Promise { - await getSymbol("Wallet_refreshAsync")(this.#walletPtr); - await this.throwIfError(); - } - - async init(): Promise { - const bool = await getSymbol("Wallet_init")( - this.#walletPtr, - CString("http://nodex.monerujo.io:18081"), - 0n, - CString(""), - CString(""), - false, - false, - CString(""), + return await fns.Wallet_refreshAsync(this.#ptr); + } + + async setupBackgroundSync( + backgroundSyncType: number, + walletPassword: string, + backgroundCachePassword: string, + ): Promise { + return await fns.Wallet_setupBackgroundSync( + this.#ptr, + backgroundSyncType, + CString(walletPassword), + CString(backgroundCachePassword), ); - await this.throwIfError(); - return bool; } - async setTrustedDaemon(value: boolean): Promise { - await getSymbol("Wallet_setTrustedDaemon")(this.#walletPtr, value); - } - - static async create( - walletManager: WalletManager, - path: string, - password: string, - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); - - const walletPtr = await getSymbol("WalletManager_createWallet")( - walletManagerPtr, - CString(path), - CString(password), - CString("English"), - 0, - ); + async startBackgroundSync(): Promise { + return await fns.Wallet_startBackgroundSync(this.#ptr); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); + async stopBackgroundSync(walletPassword: string): Promise { + return await fns.Wallet_stopBackgroundSync(this.#ptr, CString(walletPassword)); + } - return wallet; + async store(path = ""): Promise { + return await fns.Wallet_store(this.#ptr, CString(path)); } - static async open( - walletManager: WalletManager, - path: string, - password: string, - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); + async close(store: boolean): Promise { + return await fns.WalletManager_closeWallet(this.#walletManager.getPointer(), this.#ptr, store); + } - const walletPtr = await getSymbol("WalletManager_openWallet")( - walletManagerPtr, - CString(path), - CString(password), - 0, + async seed(offset = ""): Promise { + return await readCString( + await fns.Wallet_seed(this.#ptr, CString(offset)), ); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); - - return wallet; - } - - static async recover( - walletManager: WalletManager, - path: string, - password: string, - mnemonic: string, - restoreHeight: bigint, - seedOffset: string = "", - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); - - const walletPtr = await getSymbol("WalletManager_recoveryWallet")( - walletManagerPtr, - CString(path), - CString(password), - CString(mnemonic), - 0, - restoreHeight, - 1n, - CString(seedOffset), + async address(accountIndex = 0n, addressIndex = 0n): Promise { + return await readCString( + await fns.Wallet_address(this.#ptr, accountIndex, addressIndex), ); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); + async balance(accountIndex = 0): Promise { + return await fns.Wallet_balance(this.#ptr, accountIndex); + } - return wallet; + async unlockedBalance(accountIndex = 0): Promise { + return await fns.Wallet_unlockedBalance(this.#ptr, accountIndex); } - async address(accountIndex = 0n, addressIndex = 0n): Promise { - 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 ?? ""}`); - } - return await readCString(address); + async synchronized(): Promise { + return await fns.Wallet_synchronized(this.#ptr); } - async balance(accountIndex = 0): Promise { - return await getSymbol("Wallet_balance")(this.#walletPtr, accountIndex); + async blockChainHeight(): Promise { + return await fns.Wallet_blockChainHeight(this.#ptr); } - async unlockedBalance(accountIndex = 0): Promise { - return await getSymbol("Wallet_unlockedBalance")(this.#walletPtr, accountIndex); + async daemonBlockChainHeight(): Promise { + return await fns.Wallet_daemonBlockChainHeight(this.#ptr); } - status(): Promise { - return getSymbol("Wallet_status")(this.#walletPtr); + async addSubaddressAccount(label: string): Promise { + return await fns.Wallet_addSubaddressAccount(this.#ptr, CString(label)); } - async errorString(): Promise { - if (!await this.status()) return null; + async numSubaddressAccounts(): Promise { + return await fns.Wallet_numSubaddressAccounts(this.#ptr); + } - const error = await getSymbol("Wallet_errorString")(this.#walletPtr); - if (!error) return null; + async addSubaddress(accountIndex: number, label: string): Promise { + return await fns.Wallet_addSubaddress( + this.#ptr, + accountIndex, + CString(label), + ); + } - return await readCString(error) || null; + async numSubaddresses(accountIndex: number): Promise { + return await fns.Wallet_numSubaddresses( + this.#ptr, + accountIndex, + ); } - async throwIfError(sanitize = true): Promise { - const maybeError = await this.errorString(); - if (maybeError) { - if (sanitize) this.sanitizer?.(); - throw new Error(maybeError); - } + async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise { + return await readCString( + await fns.Wallet_getSubaddressLabel(this.#ptr, accountIndex, addressIndex), + ); } - async synchronized(): Promise { - const synchronized = await getSymbol("Wallet_synchronized")(this.#walletPtr); - await this.throwIfError(); - return synchronized; + async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise { + return await fns.Wallet_setSubaddressLabel(this.#ptr, accountIndex, addressIndex, CString(label)); } - async blockChainHeight(): Promise { - const height = await getSymbol("Wallet_blockChainHeight")(this.#walletPtr); - await this.throwIfError(); - return height; + async isOffline(): Promise { + return await fns.Wallet_isOffline(this.#ptr); } - async daemonBlockChainHeight(): Promise { - const height = await getSymbol("Wallet_daemonBlockChainHeight")(this.#walletPtr); - await this.throwIfError(); - return height; + async setOffline(offline: boolean): Promise { + return await fns.Wallet_setOffline(this.#ptr, offline); } - async managerBlockChainHeight(): Promise { - const height = await getSymbol("WalletManager_blockchainHeight")(this.#walletManagerPtr); - await this.throwIfError(); - return height; + async publicViewKey(): Promise { + return await readCString(await fns.Wallet_publicViewKey(this.#ptr)); } - async managerTargetBlockChainHeight(): Promise { - const height = await getSymbol("WalletManager_blockchainTargetHeight")(this.#walletManagerPtr); - await this.throwIfError(); - return height; + async secretViewKey(): Promise { + return await readCString(await fns.Wallet_secretViewKey(this.#ptr)); } - async addSubaddressAccount(label: string): Promise { - await getSymbol("Wallet_addSubaddressAccount")( - this.#walletPtr, - CString(label), - ); - await this.throwIfError(); + async publicSpendKey(): Promise { + return await readCString(await fns.Wallet_publicSpendKey(this.#ptr)); } - async numSubaddressAccounts(): Promise { - const accountsLen = await getSymbol("Wallet_numSubaddressAccounts")(this.#walletPtr); - await this.throwIfError(); - return accountsLen; + async secretSpendKey(): Promise { + return await readCString(await fns.Wallet_secretSpendKey(this.#ptr)); } - async addSubaddress(accountIndex: number, label: string): Promise { - await getSymbol("Wallet_addSubaddress")( - this.#walletPtr, - accountIndex, - CString(label), - ); - await this.throwIfError(); + async exportOutputs(fileName: string, all: boolean): Promise { + return await fns.Wallet_exportOutputs(this.#ptr, CString(fileName), all); } - async numSubaddresses(accountIndex: number): Promise { - const address = await getSymbol("Wallet_numSubaddresses")( - this.#walletPtr, - accountIndex, + async exportOutputsUR(maxFragmentLength: bigint, all: boolean): Promise { + const exportOutputsUR = fns.Wallet_exportOutputsUR; + if (!exportOutputsUR) return null; + + return await readCString( + await exportOutputsUR(this.#ptr, maxFragmentLength, all), ); - await this.throwIfError(); - return address; } - async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise { - 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 ?? ""}`); - } - return await readCString(label); + async importOutputs(fileName: string): Promise { + return await fns.Wallet_importOutputs(this.#ptr, CString(fileName)); } - async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise { - await getSymbol("Wallet_setSubaddressLabel")( - this.#walletPtr, - accountIndex, - addressIndex, - CString(label), + async importOutputsUR(input: string): Promise { + const importOutputsUR = fns.Wallet_importOutputsUR; + if (!importOutputsUR) return null; + + return await importOutputsUR(this.#ptr, CString(input)); + } + + async exportKeyImages(fileName: string, all: boolean): Promise { + return await fns.Wallet_exportKeyImages(this.#ptr, CString(fileName), all); + } + + async exportKeyImagesUR(maxFragmentLength: bigint, all: boolean): Promise { + const exportKeyImagesUR = fns.Wallet_exportKeyImagesUR; + if (!exportKeyImagesUR) return null; + + return await readCString( + await exportKeyImagesUR(this.#ptr, maxFragmentLength, all), ); - await this.throwIfError(); } - async getHistory(): Promise { - const transactionHistoryPointer = await getSymbol("Wallet_history")(this.#walletPtr); - await this.throwIfError(); - return new TransactionHistory(transactionHistoryPointer as TransactionHistoryPtr); + async importKeyImages(fileName: string): Promise { + return await fns.Wallet_importKeyImages(this.#ptr, CString(fileName)); + } + + async importKeyImagesUR(input: string): Promise { + const importKeyImagesUR = fns.Wallet_importKeyImagesUR; + if (!importKeyImagesUR) return null; + + return await importKeyImagesUR(this.#ptr, CString(input)); + } + + async loadUnsignedTx(fileName: string): Promise { + const pendingTxPtr = await fns.Wallet_loadUnsignedTx(this.#ptr, CString(fileName)); + return UnsignedTransaction.new(pendingTxPtr as UnsignedTransactionPtr); + } + + async loadUnsignedTxUR(input: string): Promise { + const loadUnsignedTxUR = fns.Wallet_loadUnsignedTxUR; + if (!loadUnsignedTxUR) return null; + + const pendingTxPtr = await loadUnsignedTxUR(this.#ptr, CString(input)); + if (await this.status()) { + throw this.errorString(); + } + return UnsignedTransaction.new(pendingTxPtr as UnsignedTransactionPtr); } async createTransaction( destinationAddress: string, amount: bigint, - pendingTransactionPriority = 0 | 1 | 2 | 3, + pendingTransactionPriority: 0 | 1 | 2 | 3, subaddressAccount: number, - sanitize = true, prefferedInputs = "", mixinCount = 0, paymentId = "", - separator = ",", - ): Promise { - const pendingTxPtr = await getSymbol("Wallet_createTransaction")( - this.#walletPtr, + ): Promise { + const pendingTxPtr = await fns.Wallet_createTransaction( + this.#ptr, CString(destinationAddress), CString(paymentId), amount, @@ -296,13 +270,61 @@ export class Wallet { pendingTransactionPriority, subaddressAccount, CString(prefferedInputs), - CString(separator), + C_SEPARATOR, + ); + + if (!pendingTxPtr) return null; + return PendingTransaction.new(pendingTxPtr as PendingTransactionPtr); + } + + async createTransactionMultDest( + destinationAddresses: string[], + amounts: bigint[], + amountSweepAll: boolean, + pendingTransactionPriority: 0 | 1 | 2 | 3, + subaddressAccount: number, + preferredInputs: string[] = [], + mixinCount = 0, + paymentId = "", + ): Promise { + const pendingTxPtr = await fns.Wallet_createTransactionMultDest( + this.#ptr, + CString(destinationAddresses.join(SEPARATOR)), + C_SEPARATOR, + CString(paymentId), + amountSweepAll, + CString(amounts.join(SEPARATOR)), + C_SEPARATOR, + mixinCount, + pendingTransactionPriority, + subaddressAccount, + CString(preferredInputs.join(SEPARATOR)), + C_SEPARATOR, ); - await this.throwIfError(sanitize); - return new PendingTransaction(pendingTxPtr as PendingTransactionPtr); + return PendingTransaction.new(pendingTxPtr as PendingTransactionPtr); + } + + async coins(): Promise { + const coinsPtr = await fns.Wallet_coins(this.#ptr); + if (!coinsPtr) return null; + + return new Coins(coinsPtr as CoinsPtr); + } + + async status(): Promise { + return await fns.Wallet_status(this.#ptr); + } + + async errorString(): Promise { + if (!await this.status()) return null; + const error = await fns.Wallet_errorString(this.#ptr); + return await readCString(error); } - async amountFromString(amount: string): Promise { - return await getSymbol("Wallet_amountFromString")(CString(amount)); + async throwIfError(): Promise { + const maybeError = await this.errorString(); + if (maybeError) { + throw new Error(maybeError); + } } } diff --git a/impls/monero.ts/src/wallet_manager.ts b/impls/monero.ts/src/wallet_manager.ts index b1fafec3..45fd0c90 100644 --- a/impls/monero.ts/src/wallet_manager.ts +++ b/impls/monero.ts/src/wallet_manager.ts @@ -1,26 +1,148 @@ -import { getSymbol, Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { CString } from "./utils.ts"; +import { Wallet, WalletPtr } from "./wallet.ts"; export type WalletManagerPtr = Deno.PointerObject<"walletManager">; export class WalletManager { #ptr: WalletManagerPtr; - sanitizer?: Sanitizer; - constructor(walletManagerPtr: WalletManagerPtr, sanitizer?: Sanitizer) { + constructor(walletManagerPtr: WalletManagerPtr) { this.#ptr = walletManagerPtr; - this.sanitizer = sanitizer; } getPointer(): WalletManagerPtr { return this.#ptr; } - static async new(sanitizer?: Sanitizer) { - const ptr = await getSymbol("WalletManagerFactory_getWalletManager")(); + static async new() { + const ptr = await fns.WalletManagerFactory_getWalletManager(); if (!ptr) { - sanitizer?.(); throw new Error("Failed retrieving wallet manager"); } - return new WalletManager(ptr as WalletManagerPtr, sanitizer); + + return new WalletManager(ptr as WalletManagerPtr); + } + + async setDaemonAddress(address: string): Promise { + return await fns.WalletManager_setDaemonAddress(this.#ptr, CString(address)); + } + + async createWallet(path: string, password: string): Promise { + const walletPtr = await fns.WalletManager_createWallet( + this.#ptr, + CString(path), + CString(password), + CString("English"), + 0, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async openWallet(path: string, password: string): Promise { + const walletPtr = await fns.WalletManager_openWallet( + this.#ptr, + CString(path), + CString(password), + 0, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverWallet( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + seedOffset: string = "", + ): Promise { + const walletPtr = await fns.WalletManager_recoveryWallet( + this.#ptr, + CString(path), + CString(password), + CString(mnemonic), + 0, + restoreHeight, + 1n, + CString(seedOffset), + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverFromPolyseed( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + passphrase = "", + ): Promise { + return await this.createFromPolyseed( + path, + password, + mnemonic, + restoreHeight, + passphrase, + false, + ); + } + + async createFromPolyseed( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + passphrase = "", + newWallet = true, + ): Promise { + const walletPtr = await fns.WalletManager_createWalletFromPolyseed( + this.#ptr, + CString(path), + CString(password), + 0, + CString(mnemonic), + CString(passphrase), + newWallet, + restoreHeight, + 1n, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverFromKeys( + path: string, + password: string, + restoreHeight: bigint, + address: string, + viewKey: string, + spendKey: string, + ): Promise { + const walletPtr = await fns.WalletManager_createWalletFromKeys( + this.#ptr, + CString(path), + CString(password), + CString("English"), + 0, + restoreHeight, + CString(address), + CString(viewKey), + CString(spendKey), + 0n, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; } } diff --git a/tests/compare.ts b/tests/compare.ts index d09bdd93..2fd27b8e 100755 --- a/tests/compare.ts +++ b/tests/compare.ts @@ -1,80 +1,34 @@ -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"; +import { + loadMoneroDylib, + loadWowneroDylib, + moneroSymbols, + WalletManager, + wowneroSymbols, +} from "../impls/monero.ts/mod.ts"; + 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 `MONERO${infer Y}` ? `WOWNERO${Y}` : never; -type WowneroSymbols = { [Key in keyof typeof moneroSymbols as ReplaceMonero]: (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; - 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())); + loadMoneroDylib(dylib); } 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()), - ); + loadWowneroDylib(dylib); } const walletManager = await WalletManager.new(); -const wallet = await Wallet.open(walletManager, walletInfo.path, walletInfo.password); +const wallet = await walletManager.openWallet(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 wallet.publicSpendKey(), walletInfo.publicSpendKey); +assertEquals(await wallet.secretSpendKey(), walletInfo.secretSpendKey); -assertEquals(await getKey(wallet, "publicViewKey"), walletInfo.publicViewKey); -assertEquals(await getKey(wallet, "secretViewKey"), walletInfo.secretViewKey); +assertEquals(await wallet.publicViewKey(), walletInfo.publicViewKey); +assertEquals(await wallet.secretViewKey(), walletInfo.secretViewKey); await wallet.store(walletInfo.path); diff --git a/tests/deno.lock b/tests/deno.lock old mode 100755 new mode 100644 index 02d189fe..b67d77d4 --- a/tests/deno.lock +++ b/tests/deno.lock @@ -12,6 +12,7 @@ "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.8": "1.0.8", "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", @@ -71,6 +72,9 @@ "@std/path@1.0.6": { "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" }, + "@std/path@1.0.8": { + "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" + }, "@std/streams@0.221.0": { "integrity": "47f2f74634b47449277c0ee79fe878da4424b66bd8975c032e3afdca88986e61", "dependencies": [ diff --git a/tests/integration.test.ts b/tests/integration.test.ts new file mode 100644 index 00000000..1a650097 --- /dev/null +++ b/tests/integration.test.ts @@ -0,0 +1,632 @@ +import { + CoinsInfo, + type Dylib, + loadMoneroDylib, + loadWowneroDylib, + moneroSymbols, + Wallet, + WalletManager, + wowneroSymbols, +} from "../impls/monero.ts/mod.ts"; + +import { assert, assertEquals } from "jsr:@std/assert"; +import { $, downloadCli, getMoneroC } from "./utils.ts"; + +const coin = Deno.env.get("COIN"); +if (coin !== "monero" && coin !== "wownero") { + throw new Error("COIN env var invalid or missing"); +} + +async function syncBlockchain(wallet: Wallet): Promise { + // Wait for blockchain to sync + const blockHeight = await new Promise((resolve) => { + let timeout: number; + + const poll = async () => { + const blockChainHeight = await wallet.blockChainHeight(); + const daemonBlockchainHeight = await wallet.daemonBlockChainHeight(); + // console.log("Blockchain height:", blockChainHeight, "Daemon blockchain height:", daemonBlockchainHeight, "Remains:", daemonBlockchainHeight - blockChainHeight); + if (blockChainHeight === daemonBlockchainHeight) { + clearTimeout(timeout); + resolve(blockChainHeight); + } else { + setTimeout(poll, 500); + } + }; + + poll(); + }); + await new Promise((r) => setTimeout(r, 1500)); // wait for it to sync + return blockHeight; +} + +// TODO: Change for custom address on CI +const WOWNERO_NODE_URL = "https://node3.monerodevs.org:34568"; +const MONERO_NODE_URL = "https://nodes.hashvault.pro:18081"; +const NODE_URL = coin === "monero" ? MONERO_NODE_URL : WOWNERO_NODE_URL; + +const WOWNERO_DESTINATION_ADDRESS = + "WW3Zetw4Gg5Rk88ViCm8H8Ft8BqgAQ5DbTLZC1whv8GNFJPSoGfLViW3dAAb4Bcqpz2M1y31pZykd4ZKd8GH1UyF1fwEFg5mS"; +const MONERO_DESTINATION_ADDRESS = + "89BoVWjqdGVe68wdxbYurXR8sXaEb96eWKYRPxdT6wSCfZYK6XSHoj5ZRXQLtd7GzL2B2PD7Lb7GSKupkXMWjQVFAEb1CK8"; +const DESTINATION_ADDRESS = coin === "monero" ? MONERO_DESTINATION_ADDRESS : WOWNERO_DESTINATION_ADDRESS; + +const BILLION = 10n ** 9n; + +await getMoneroC(coin, "next"); + +interface WalletInfo { + name: string; + password: string; + seed: string; + offset?: string; + address: string; + restoreHeight: bigint; + + publicSpendKey: string; + secretSpendKey: string; + publicViewKey: string; + secretViewKey: string; +} + +async function clearWallets() { + await Deno.remove("tests/wallets/", { recursive: true }).catch(() => {}); + await Deno.mkdir("tests/wallets/"); +} + +let dylib: Dylib; +if (coin === "monero") { + dylib = Deno.dlopen(`tests/libs/next/monero_libwallet2_api_c.so`, moneroSymbols); + loadMoneroDylib(dylib); +} else { + dylib = Deno.dlopen(`tests/libs/next/wownero_libwallet2_api_c.so`, wowneroSymbols); + loadWowneroDylib(dylib); +} + +Deno.test("0001-polyseed.patch", async (t) => { + const WALLETS: Record<"monero" | "wownero", WalletInfo[]> = { + monero: [ + //#region Cake wallet, no offset + { + name: "English Wallet", + password: "englishwallet", + seed: + "tortoise winter play argue laptop diary tell library travel cupboard view river embark rubber plunge student", + restoreHeight: 3254619n, + address: "49PL6qHMkc4Hw3dWT5wy5NbbVd2xda8zw3tLx2BoQsNZUWDQYYpwMEKjB9BLbEKSo3S3z34bncFw6ijToTwfiEJJ5m8aefx", + publicSpendKey: "ccd6846ab69fdd653a8d092d89590dced40aa2862f3c24113fedfcd6469162a4", + secretSpendKey: "37fd2e3e933c8468beb407e5350789e23bed5df33eeeb35d3b119401988e6709", + publicViewKey: "6f0de7385aafd4fc259cbd0abb069295c5d3824b7e1b81f97ffcf8cccde6c72a", + secretViewKey: "b8095208d61fc22e4ee3a79347a889e4872cdcf1cceff991542834cef5375907", + }, + { + name: "Chinese Traditional Wallet", + password: "chinesetraditionalwallet", + seed: "旗 铁 革 酯 紧 毅 饱 应 第 兄 植 隙 点 吐 童 赞", + restoreHeight: 3254619n, + address: "4AR8YW51Ga3DR4a47F5J8rXaqyBa8pnnF557pTyt52ZqNMFa3gfxvi13R7sbt5zHfjbF5aKsLFZQrRod3qcr5MQj4f91rLh", + publicSpendKey: "e80bab7b3e2d384a393b825cf4f2abb6d8d08f1742d87c1856c064a609a0647f", + secretSpendKey: "c7de78f9819db6755e14d2e1411f1591c2d0b3a6ee19049c30e270f81eb50401", + publicViewKey: "a366527c3a6d160e717ebaac11d08eccb95586991a4c87944ad750331adac020", + secretViewKey: "aab2ea0dc6fa2745c8c7113399242c03300664a21cac9202c315ad789b67d004", + }, + { + name: "Chinese Simplified Wallet", + password: "chinesesimplifiedwallet", + seed: "纹 触 集 驶 朋 辨 你 版 是 益 驳 修 偏 汽 录 吨", + restoreHeight: 3254619n, + address: "47E7p1mFGNj59QNfjpXcopP1YZuGdn7NhYJv25xbPKdicnThww5DUv2aNMH7oPWsKZjQQmXMkBzUze2T6gAaXafLAF9E4Dz", + publicSpendKey: "93dec0155d30b818c7d64a0b0c3a678395e3e28bdd736abb2ef7c360b38449d5", + secretSpendKey: "60084b4ff3d99d6ca38876078721c13692635429c74ef5d71f03310cf2e0690b", + publicViewKey: "eff74761051a2fc77eac4d7ed1b564fd83c0e3e924199bdd5ba66b4bf4ecf351", + secretViewKey: "3bde0d3a1bd2877f75fc9f6ea331c976d26a62e469db1d11ddaef2cce9405d02", + }, + { + name: "Japanese Wallet", + password: "japanesewallet", + seed: + "きほん ことば そうび きどう なまえ ひさしぶり ごうけい ふひょう ぎゅうにく しはらい きびしい はんとし ととのえる たかい とかい るりがわら", + restoreHeight: 3254619n, + address: "45YYsW5do7NjGSaRZPAkPQ9mi4HQsMv3w86HFddtkZi4cCbqtFiVoqJjdFobCtCwpBPZWSnUtmrU2G9fLpEE7vsQU3aZyeE", + publicSpendKey: "677ac034b7c3e9fcb178370e3df067346ff5703db8273e2a65010862a85935d2", + secretSpendKey: "1fe9110cc46c58ed0f5cb6c6e189e246f6c3cfbf459de2ed1565838b6e08780e", + publicViewKey: "7276d8537e0cd9fed6b9327177ed9086e159f7d63b9fb35a92693cdbf322ffef", + secretViewKey: "f3c8f4121cdf07616453f005e7f2cf0474cf3608ec632f4e81165ec1c5b4aa0c", + }, + { + name: "Korean Wallet", + password: "koreanwallet", + seed: "단골 운전 일대 제작 구역 보자기 대한민국 답장 쇼핑 논문 편견 대전 충돌 강당 형제 볼펜", + restoreHeight: 3254619n, + address: "42DNYppLMXki8j3urYuXaTU1S9EBrSJ71aVNbzr4fKfnXryMwJ2rFt6Y5eCP9vpej9AdrZqNFXDFB1VmkjzjX5e5URJ9q8c", + publicSpendKey: "0f96caeac7cd4ff5eb55810b4eb87ca1779637e949d138c837fe9a776b3c51b8", + secretSpendKey: "d86c40f13694e7511499cdb22db4e96ee2990cfffde52274248bb8818a36c406", + publicViewKey: "826438a35b0f63b9d0b877269a0468399b3f60513e079602f73c4b0f7cd0e6f3", + secretViewKey: "3773b61c0cac3a2e15d03b989461704c6e90916561c6b98d035bd3d2caf88c05", + }, + { + name: "Portugese Wallet", + password: "portugesewallet", + seed: + "inscrito raposa vermelho medusa apetite bacharel quantia usado poupar pilotar sigilo ideia robalo ignorado desgaste intimar", + restoreHeight: 3254619n, + address: "43xw29tpLnU5VaPZ8Nuzz7DqrnUip5tmWBn3aukkZAyzgHTscJHvESy6pmYutLebQAB9TJgGhoCAWhPK6WJ39CpiD29fbwj", + publicSpendKey: "3dcbbde593acc11adc291eeeec8bc44cc7943192d54fb1406de435b4d7b73dea", + secretSpendKey: "9b94f57038b07b8280f27cedbed6e53472b48fe4683de15cc9e034fdddfcbb05", + publicViewKey: "dce02ef2ca595e22d1242a53b5235f3ca8508e22386d03f171bc77ef856e1b6a", + secretViewKey: "27bdd486a74f9b7c7a79c39c8ff7438a3fc862bb1ec3f6ef035a8acedf876705", + }, + { + name: "Czech Wallet", + password: "czechwallet", + seed: + "ulice louskat odmlka parodie dominant slanina sukno vodivost zimnice vykonat sundat kalibr dobro moucha kometa legenda", + restoreHeight: 3254619n, + address: "499LrJgGPkFA1BPvF4xqr5bwZfKyCZah1C2CEhvqpW7YGQddwWYyR2L1F1TJhqyxxwa4TXKYZM4bb8ukq3kein1cPMNLLi4", + publicSpendKey: "c6796799947e0c35d3720af264d6a6d0e5a574cea1cde441e343b828a00c875c", + secretSpendKey: "61f9c86a71744a101c36b5628c1c4324e49e84861f136cbd2d0f1477ba5ace0b", + publicViewKey: "1d75ed535e3dfd0171a4a714aef368c5a6862aaf9972422f49cd76d232f88fc6", + secretViewKey: "47fd8eadc1848a09b343815a74185835a0b2cc8567e61022322dd52f6aaf2004", + }, + { + name: "Spanish Wallet", + password: "spanishwallet", + seed: + "rehén torpedo remo existir fuente dama culpa riqueza cebolla supremo vereda odio novio sumar espía margen", + restoreHeight: 3254619n, + address: "41xeBwVJEpVVrJntrvUXafJR4mk9x3tfW3zXxamh8G951SM9BbBLgJSgzwdCywLRrbZvipLL2Azu9jKbu4Y9Hey3MUF6f19", + publicSpendKey: "08e31c09ad35beac7bcb9e4a689e40681df1b42cd2686511e344d02606bfc802", + secretSpendKey: "19ae689bdd594fde5d06b66e4c7a89d9783c02694ae615e33928b38f0c52120c", + publicViewKey: "9cdf4adfd9c7c7ef236c9084a0ceb4c4da6017b9fd1b6cfd04e02527ea6f06b5", + secretViewKey: "40ae98266cadfbf546e861cac2b2e93ce921f9db2e02f072d86aea274a182006", + }, + { + name: "Italian Wallet", + password: "italianwallet", + seed: + "fuso rinvenire astenuto camicia erboso icona bollito esito spettrale abisso dogma appunto prefisso gracile podismo araldica", + restoreHeight: 3254619n, + address: "46Kkbh8jLorHRKJEV7C4hNczZknHeB6w4GHpLWkpfThMdPLeeU8MFRmDfMqioYyacCTAG9wZ9y9UHZDNhehEssDVHePj3Ja", + publicSpendKey: "7c0b928d9d8349622a07673a9721e9d72f60ccfc8e83935b699c379999104cd9", + secretSpendKey: "673b61bdb369cc61a022e0eab47e5e41cf86e99a80979f75ebcda1219ee0ab01", + publicViewKey: "8858e908f756c44bb286d5b657824d9c660386c7db0b0ec0974d268a7432bc93", + secretViewKey: "5012dbfa9e1cd2a30c2a18654dc9c8bee128af8fe7246c46e5f4def76705fe0f", + }, + //#endregion + //#region Feather wallet, offset + { + name: "English Wallet (offset)", + password: "englishwallet", + seed: "loud fix cattle broken right main web rather write aunt left nation broken ship program ten", + offset: "englishoffset", + restoreHeight: 3263855n, + address: "4B2QGWy9as7bwwLNq2DQ26Q3woahpTLbR7d8vJE1uKL5gobU9iMydFqbVrYa9ixfrnAvnuwT9BXpkBx1APocbJfb2drFuQi", + publicSpendKey: "f817ca86625d1ed0ef81ccb8a4e82b89cfc3345512c7b82798ba2f5982b7daed", + secretSpendKey: "39ae15e92e08a0903652b4b0f187d740d2a5bf08e77879babb345b9a78ca6504", + publicViewKey: "f7fb585b9a288cce3f3b1a5f0ca6873b5a2fef8afb3bd94174ac31cbed53620e", + secretViewKey: "d5676e49438b0cd38c6a699ab783c11f21e1a7ebc1c9174121e37456a97f380d", + }, + //#endregion + ], + wownero: [ + { + name: "English Wowlet", + password: "englishwallet", + seed: "fragile proud oven shove trend visit oxygen dove pledge entire pencil exist throw type large chase", + restoreHeight: 0n, + address: "Wo4ExnCfajHZcVY9Q4XgjTYHu9GwyT9E7dZQuoFhY7HNWr2X6iD2wuB1asHQ1DVEtNYSLjqiCzJVDg5ZKeWnbKDe1LD9Wwy91", + publicSpendKey: "88c53568fd38c2f957f229a7d4dabb142e4fcfd7c128da922b63e4f4df25b26e", + secretSpendKey: "b131442ee0aecd410947a74bf622e2833397f372ee843fc3d291cb16a343e308", + publicViewKey: "ea7a909bc832037db14c6a537357bbf2eedee84b7d00e9a2b1d718d92fe52693", + secretViewKey: "96e03a70a4956656be6cc1fa2252f159ae0b2d2fdc21f761fc7e8d0316931708", + }, + // TODO: Add other localized wallets for Wownero + ], + }; + + await clearWallets(); + + for (const walletInfo of WALLETS[coin]) { + await t.step(walletInfo.name, async () => { + const walletManager = await WalletManager.new(); + const path = `tests/wallets/${walletInfo.name}`; + + const wallet = await walletManager.recoverFromPolyseed( + path, + walletInfo.password, + walletInfo.seed, + walletInfo.restoreHeight, + walletInfo.offset, + ); + + await wallet.init({}); // empty daemon address for offline test + + assertEquals(await wallet.address(), walletInfo.address); + + assertEquals(await wallet.publicSpendKey(), walletInfo.publicSpendKey); + assertEquals(await wallet.secretSpendKey(), walletInfo.secretSpendKey); + + assertEquals(await wallet.publicViewKey(), walletInfo.publicViewKey); + assertEquals(await wallet.secretViewKey(), walletInfo.secretViewKey); + + await wallet.close(true); + }); + } +}); + +Deno.test("0002-wallet-background-sync-with-just-the-view-key.patch", async () => { + await clearWallets(); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.createWallet("tests/wallets/squirrel", "belka"); + await wallet.init({ + address: NODE_URL, + }); + + const walletInfo = { + address: await wallet.address(), + publicSpendKey: await wallet.publicSpendKey(), + secretSpendKey: await wallet.secretSpendKey(), + publicViewKey: await wallet.publicViewKey(), + secretViewKey: await wallet.secretViewKey(), + }; + + await wallet.setupBackgroundSync(2, "belka", "background-belka"); + await wallet.startBackgroundSync(); + await wallet.close(true); + + const backgroundWallet = await walletManager.openWallet( + "tests/wallets/squirrel.background", + "background-belka", + ); + await backgroundWallet.init({ address: NODE_URL }); + + const blockChainHeight = await syncBlockchain(backgroundWallet); + await backgroundWallet.refreshAsync(); + + await backgroundWallet.close(true); + + const reopenedWallet = await walletManager.openWallet("tests/wallets/squirrel", "belka"); + await reopenedWallet.throwIfError(); + await reopenedWallet.refreshAsync(); + + assertEquals(await reopenedWallet.blockChainHeight(), blockChainHeight); + assertEquals( + walletInfo, + { + address: await reopenedWallet.address(), + publicSpendKey: await reopenedWallet.publicSpendKey(), + secretSpendKey: await reopenedWallet.secretSpendKey(), + publicViewKey: await reopenedWallet.publicViewKey(), + secretViewKey: await reopenedWallet.secretViewKey(), + }, + ); + + await reopenedWallet.close(true); +}); + +Deno.test("0004-coin-control.patch", { + ignore: coin === "wownero" || !( + Deno.env.get("SECRET_WALLET_PASSWORD") && + Deno.env.get("SECRET_WALLET_MNEMONIC") && + Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT") + ), +}, async (t) => { + await clearWallets(); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.recoverFromPolyseed( + "tests/wallets/secret-wallet", + Deno.env.get("SECRET_WALLET_PASSWORD")!, + Deno.env.get("SECRET_WALLET_MNEMONIC")!, + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!), + ); + + assertEquals( + await wallet.address(), + "434dZdLzhymcoNyGSBUJAqhDCLtBECN6698CGRMYByuEAYtpxXdbiibQb3t4qX3SiZi9vDWkxeiEF8kmDGmEoEZ4VMG8Nvh", + ); + + await wallet.init({ address: NODE_URL }); + await wallet.refreshAsync(); + + // Wait for blockchain to sync + await syncBlockchain(wallet); + + await wallet.refreshAsync(); + await wallet.store(); + await wallet.refreshAsync(); + + const coins = (await wallet.coins())!; + await coins.refresh(); + + // COINS: + // 5x 0.001XMR 1x 0.005XMR (in no particular order) + await t.step("preffered_inputs", async (t) => { + const coinsCount = await coins.count(); + + const availableCoinsData: Record = { + ["0.001"]: [], + ["0.005"]: [], + }; + + const freezeAll = async () => { + for (const [_, coinsData] of Object.entries(availableCoinsData)) { + for (const coinData of coinsData) { + await coins.setFrozen(coinData.index); + } + } + await coins.refresh(); + }; + + const thawAll = async () => { + for (const [_, coinsData] of Object.entries(availableCoinsData)) { + for (const coinData of coinsData) { + await coins.thaw(coinData.index); + } + } + await coins.refresh(); + }; + + let availableCoinsCount = 0; + let totalAvailableAmount = 0n; + for (let i = 0; i < coinsCount; ++i) { + const coin = (await coins.coin(i))!; + if (coin.spent) { + continue; + } + + let humanReadableAmount: string; + if (coin.amount === BILLION) { + humanReadableAmount = "0.001"; + } else if (coin.amount === 5n * BILLION) { + humanReadableAmount = "0.005"; + } else { + throw new Error("Invalid coin amount! Only 5x0.01XMR coins and 1x0.05XMR coin should be available"); + } + + availableCoinsData[humanReadableAmount].push({ + index: i, + coin, + keyImage: coin.keyImage, + amount: coin.amount, + }); + + totalAvailableAmount += coin.amount; + availableCoinsCount += 1; + + await coins.thaw(i); + } + + await coins.refresh(); + + assertEquals(availableCoinsCount, 6); + assertEquals(totalAvailableAmount, 10n * BILLION); + + await t.step("Try to spend 0.002XMR by using only one 0.001XMR coin", async () => { + const transaction = await wallet.createTransaction( + DESTINATION_ADDRESS, + 2n * BILLION, + 0, + 0, + availableCoinsData["0.001"][0].keyImage!, + ); + + if (!transaction) { + throw new Error("Failed creating a transaction"); + } + + assertEquals(await transaction.status(), 1); + }); + + await t.step("Try to spend 0.002XMR with only 0.001XMR unlocked balance", async () => { + await freezeAll(); + await coins.thaw(availableCoinsData["0.001"][0].index); + + const transaction = await wallet.createTransaction(DESTINATION_ADDRESS, 2n * BILLION, 0, 0); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assert((await transaction.errorString())?.includes("not enough money to transfer")); + + await thawAll(); + }); + + await t.step("Try to spend 0.002XMR + fee with only 0.002XMR unlocked balance", async () => { + await freezeAll(); + await coins.thaw(availableCoinsData["0.001"][0].index); + await coins.thaw(availableCoinsData["0.001"][1].index); + + const transaction = await wallet.createTransaction( + DESTINATION_ADDRESS, + 2n * BILLION, + 0, + 0, + availableCoinsData["0.001"][0].keyImage!, + ); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assertEquals( + await transaction.errorString(), + "not enough money to transfer, overall balance only 0.002000000000, sent amount 0.002000000000", + ); + }); + + await thawAll(); + }); + + await t.step("spend more than unfrozen balance", async () => { + const unlockedBalance = await wallet.unlockedBalance(); + const transaction = await wallet.createTransaction(DESTINATION_ADDRESS, unlockedBalance + 1n, 0, 0); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assert( + await transaction.errorString(), + "not enough money to transfer, overall balance only 0.001000000000, sent amount 0.001000000001", + ); + }); + + await wallet.close(true); +}); + +Deno.test("0009-Add-recoverDeterministicWalletFromSpendKey.patch", async () => { + await Promise.all([ + downloadCli(coin), + clearWallets(), + ]); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.createWallet("tests/wallets/stoat", "gornostay"); + const moneroCSeed = await wallet.seed(); + await wallet.close(true); + + await Deno.remove("./tests/wallets/stoat"); + + const cliPath = `./tests/${coin}-cli/${coin}-wallet-cli`; + const moneroCliSeed = (await $.raw`${cliPath} --wallet-file ./tests/wallets/stoat --password gornostay --command seed` + .stdinText(`gornostay\n`) + .lines()).slice(-3).join(" "); + + assertEquals(moneroCSeed, moneroCliSeed); +}); + +Deno.test("0012-WIP-UR-functions.patch", { + ignore: coin === "wownero" || !( + Deno.env.get("SECRET_WALLET_PASSWORD") && + Deno.env.get("SECRET_WALLET_MNEMONIC") && + Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT") + ), +}, async (t) => { + for (const method of ["UR", "file"] as const) { + await clearWallets(); + + const walletManager = await WalletManager.new(); + + const airgap = await walletManager.recoverFromPolyseed( + "tests/wallets/secret-wallet", + Deno.env.get("SECRET_WALLET_PASSWORD")!, + Deno.env.get("SECRET_WALLET_MNEMONIC")!, + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!), + ); + await airgap.init({ address: "" }); + + const online = await walletManager.recoverFromKeys( + "tests/wallets/horse-online", + "loshad-online", + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!) - 2000n, + (await airgap.address())!, + (await airgap.secretViewKey())!, + "", + ); + await online.init({ address: NODE_URL }); + await online.refreshAsync(); + + await syncBlockchain(online); + + await online.refreshAsync(); + await online.store(); + await online.refreshAsync(); + + if (method === "UR") { + await t.step({ + name: "Sync wallets (UR)", + ignore: coin === "wownero", // Wownero doesn't have UR methods + fn: async () => { + try { + const outputs = await online.exportOutputsUR(130n, false); + await airgap.importOutputsUR(outputs!); + + const keyImages = await airgap.exportKeyImagesUR(130n, false); + await online.importKeyImagesUR(keyImages!); + } catch { + const outputs = await online.exportOutputsUR(130n, true); + await airgap.importOutputsUR(outputs!); + + const keyImages = await airgap.exportKeyImagesUR(130n, true); + await online.importKeyImagesUR(keyImages!); + } + }, + }); + + await t.step({ + name: "Transaction (UR)", + ignore: coin === "wownero", + fn: async () => { + const transaction = await online.createTransaction(DESTINATION_ADDRESS, 1n * BILLION, 0, 0); + if (!transaction) { + throw new Error("Failed creating online transaction: " + await online.errorString()); + } + + const input = await transaction.commitUR(130); + + const unsignedTx = (await airgap.loadUnsignedTxUR(input!))!; + if (!unsignedTx) { + throw new Error("Failed creating unsigned transaction: " + await online.errorString()); + } + + assertEquals(await unsignedTx.status(), 0); + assertEquals(unsignedTx.recipientAddress, DESTINATION_ADDRESS); + assert(!isNaN(Number(unsignedTx.fee))); + assertEquals(unsignedTx.amount, "1000000000"); + + await unsignedTx.signUR(130); + assertEquals(await unsignedTx.status(), 0, (await unsignedTx.errorString())!); + }, + }); + } else { + await t.step("Sync wallets (File)", async () => { + try { + await online.exportOutputs("tests/wallets/outputs", false); + await airgap.importOutputs("tests/wallets/outputs"); + + await airgap.exportKeyImages("tests/wallets/keyImages", false); + await online.importKeyImages("tests/wallets/keyImages"); + } catch { + await online.exportOutputs("tests/wallets/outputs", true); + await airgap.importOutputs("tests/wallets/outputs"); + + await airgap.exportKeyImages("tests/wallets/keyImages", true); + await online.importKeyImages("tests/wallets/keyImages"); + } + }); + + await t.step("Transaction (File)", async () => { + const transaction = await online.createTransaction(DESTINATION_ADDRESS, 1n * BILLION, 0, 0); + if (!transaction) { + throw new Error("Failed creating online transaction: " + await online.errorString()); + } + + await transaction.commit("tests/wallets/transaction", false); + + const unsignedTx = await airgap.loadUnsignedTx("tests/wallets/transaction"); + if (!unsignedTx) { + throw new Error("Failed creating unsigned transaction: " + await online.errorString()); + } + + assertEquals(await unsignedTx.status(), 0); + assertEquals(unsignedTx.amount, "1000000000"); + assertEquals(unsignedTx.recipientAddress, DESTINATION_ADDRESS); + assert(!isNaN(Number(unsignedTx.fee))); + + await unsignedTx.sign("tests/wallets/signed-transaction"); + assertEquals(await unsignedTx.status(), 0); + }); + } + } +}); diff --git a/tests/utils.ts b/tests/utils.ts index 028e0ff8..cd052320 100755 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -14,9 +14,9 @@ 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 $`wget -q -o /dev/null ${MONERO_WALLET_CLI_URL}`; await $ - .raw`tar -xvf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`; + .raw`tar -xf ${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`; } @@ -25,9 +25,9 @@ export async function downloadWowneroCli() { 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 $`wget -q -o /dev/null ${WOWNERO_WALLET_CLI_URL}`; await $ - .raw`tar -xvf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`; + .raw`tar -xf ${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`; }