diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index 20dcbc5b..733785ac 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -160,6 +160,8 @@ jobs: uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.coin }} + - name: Unshallow submodules + run: git submodule foreach --recursive 'git fetch --unshallow || echo "Not a shallow submodule"' - name: Cache built if: ${{ !startsWith(github.ref, 'refs/tags/v') }} uses: actions/cache@v4 @@ -197,7 +199,7 @@ jobs: matrix: coin: [monero, wownero, zano] name: macos build - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout monero_c repo uses: actions/checkout@v4 @@ -207,10 +209,10 @@ jobs: submodules: recursive - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.4' + xcode-version: '16.1' - name: install dependencies run: | - brew install ccache binutils pigz autoconf automake libtool pkg-config + brew install ccache binutils pigz autoconf automake libtool pkg-config git - name: Patch sources run: | git config --global --add safe.directory '*' @@ -221,6 +223,8 @@ jobs: uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.coin }} + - name: Unshallow submodules + run: git submodule foreach --recursive 'git fetch --unshallow || echo "Not a shallow submodule"' - name: Cache built if: ${{ !startsWith(github.ref, 'refs/tags/v') }} uses: actions/cache@v4 @@ -271,7 +275,7 @@ jobs: xcode-version: '16.1' - name: install dependencies run: | - brew install ccache cmake autoconf automake libtool + brew install ccache cmake autoconf automake libtool git - name: Patch sources run: | git config --global --add safe.directory '*' @@ -456,7 +460,7 @@ jobs: needs: [ lib_macos ] - runs-on: macos-14 + runs-on: macos-15 steps: - uses: denoland/setup-deno@v2 with: @@ -515,7 +519,7 @@ jobs: needs: [ lib_macos ] - runs-on: macos-14 + runs-on: macos-15 steps: - uses: denoland/setup-deno@v2 with: diff --git a/apply_patches.sh b/apply_patches.sh index 8013c9db..08c18188 100755 --- a/apply_patches.sh +++ b/apply_patches.sh @@ -6,14 +6,15 @@ repo="$1" if [[ "x$repo" == "x" ]]; then - echo "Usage: $0 monero/wownero" + echo "Usage: $0 monero/wownero/zano" exit 1 fi -if [[ "x$repo" != "xwownero" && "x$repo" != "xmonero" ]]; +if [[ "x$repo" != "xwownero" && "x$repo" != "xmonero" && "x$repo" != "xzano" ]]; then - echo "Usage: $0 monero/wownero" + echo "Usage: $0 monero/wownero/zano" echo "Invalid target given, only monero and wownero are supported targets" + exit 1 fi if [[ ! -d "$repo" ]] diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart index 603eb219..78a87485 100644 --- a/impls/monero.dart/lib/src/checksum_monero.dart +++ b/impls/monero.dart/lib/src/checksum_monero.dart @@ -1,4 +1,4 @@ // ignore_for_file: constant_identifier_names -const String wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; -const String wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const String wallet2_api_c_h_sha256 = "6c1ba9b57cb185c6dad030b15bcffe8a4772f33930e7f1d62d23b33514ba6f62"; +const String wallet2_api_c_cpp_sha256 = "1d1deff340408541f5755b4838d06345f63dcdfffe26b14dbdce32a5de839c55-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; const String wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; diff --git a/impls/monero.rs/.gitignore b/impls/monero.rs/.gitignore new file mode 100644 index 00000000..2f7896d1 --- /dev/null +++ b/impls/monero.rs/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/impls/monero.rs/Cargo.lock b/impls/monero.rs/Cargo.lock new file mode 100644 index 00000000..9991a450 --- /dev/null +++ b/impls/monero.rs/Cargo.lock @@ -0,0 +1,436 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "monero_c_rust" +version = "0.0.1" +dependencies = [ + "bindgen", + "mockall", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/impls/monero.rs/Cargo.toml b/impls/monero.rs/Cargo.toml new file mode 100644 index 00000000..f0552251 --- /dev/null +++ b/impls/monero.rs/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "monero_c_rust" +version = "0.0.1" +edition = "2021" +description = "monero_c Rust bindings." +repository = "https://github.com/MrCyjaneK/monero_c" +license = "MIT" +build = "build.rs" + +[lib] +name = "monero_c_rust" +path = "src/lib.rs" +crate-type = ["lib", "cdylib"] + +[dependencies] +mockall = "0.13.0" +tempfile = "3.13.0" + +[build-dependencies] +bindgen = "0.70.1" + diff --git a/impls/monero.rs/README.md b/impls/monero.rs/README.md new file mode 100644 index 00000000..e9e8da94 --- /dev/null +++ b/impls/monero.rs/README.md @@ -0,0 +1,31 @@ +# `monero_rust` +Proof of concept `monero_c` bindings for Rust. + +## Getting started + +### Build or download `monero_c` library +Build or download the `monero_c` library for your architecture. Follow the +upstream docs at https://github.com/MrCyjaneK/monero_c or download the latest +release from https://github.com/MrCyjaneK/monero_c/releases. The library must +be placed in `../../../../release` (as in `monero_c/release`) and its name +should match your platform as in: +- Android: `libmonero_libwallet2_api_c.so` +- iOS: `MoneroWallet.framework/MoneroWallet` +- Linux: `monero_libwallet2_api_c.so` +- macOS: `monero_libwallet2_api_c.dylib` +- Windows: `monero_libwallet2_api_c.dll` + +### Run demo +With the library in a supported location, from `monero_c/impls/monero_rust`: +``` +cargo run +``` + +## Using `monero_rust` in your own crate +Refer to the `example` folder. Library placement is the same as for the demo. diff --git a/impls/monero.rs/build.rs b/impls/monero.rs/build.rs new file mode 100644 index 00000000..4d3af46c --- /dev/null +++ b/impls/monero.rs/build.rs @@ -0,0 +1,138 @@ +use bindgen::EnumVariation; +use std::env; +use std::fs::{self, OpenOptions}; +use std::io::Write; +use std::path::PathBuf; + +#[cfg(unix)] +use std::os::unix::fs as unix_fs; + +#[cfg(target_os = "windows")] +use std::fs::copy; + +fn main() { + let header_path = "../../monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h"; + let lib_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../../release"); + + // Set library names based on target OS. + // + // This rigamarole is currently required because the "monero_libwallet2_api_c" library is also + // required under the names "wallet2_api_c" and "libmonero_libwallet2_api_c" by various parts of + // the stack. This is a temporary workaround until the library is refactored to use a single + // name consistently. + let original_lib = if cfg!(target_os = "windows") { + lib_path.join("monero_libwallet2_api_c.dll") + } else if cfg!(target_os = "macos") { + lib_path.join("monero_libwallet2_api_c.dylib") + } else { + lib_path.join("monero_libwallet2_api_c.so") + }; + + let symlink_1 = if cfg!(target_os = "windows") { + lib_path.join("wallet2_api_c.dll") + } else if cfg!(target_os = "macos") { + lib_path.join("libwallet2_api_c.dylib") + } else { + lib_path.join("libwallet2_api_c.so") + }; + + let symlink_2 = if cfg!(target_os = "windows") { + lib_path.join("monero_wallet2_api_c.dll") + } else if cfg!(target_os = "macos") { + lib_path.join("libmonero_libwallet2_api_c.dylib") + } else { + lib_path.join("libmonero_libwallet2_api_c.so") + }; + + // On Unix-like systems, create symlinks. + #[cfg(unix)] + { + if original_lib.exists() && !symlink_1.exists() { + unix_fs::symlink(&original_lib, &symlink_1) + .expect("Failed to create symbolic link for libwallet2_api_c.so"); + } + + if original_lib.exists() && !symlink_2.exists() { + unix_fs::symlink(&original_lib, &symlink_2) + .expect("Failed to create symbolic link for libmonero_libwallet2_api_c.so"); + } + } + + // On Windows, copy the files instead of symlinking. + #[cfg(target_os = "windows")] + { + if original_lib.exists() && !symlink_1.exists() { + copy(&original_lib, &symlink_1).expect("Failed to copy DLL file to wallet2_api_c.dll"); + } + + if original_lib.exists() && !symlink_2.exists() { + copy(&original_lib, &symlink_2) + .expect("Failed to copy DLL file to monero_wallet2_api_c.dll"); + } + } + + println!("cargo:rerun-if-changed={}", header_path); + println!("cargo:rerun-if-changed=build.rs"); + + println!("cargo:rustc-link-search=native={}", lib_path.display()); + println!("cargo:rustc-link-lib=dylib=monero_libwallet2_api_c"); + // println!("cargo:rustc-link-lib=dylib=libc++"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_path.display()); + + // Generate bindings using bindgen. + let bindings = bindgen::Builder::default() + .header(header_path) + .allowlist_function("MONERO_.*") + .allowlist_var("MONERO_.*") + .allowlist_var("NetworkType_.*") + .allowlist_var("PendingTransactionStatus_.*") + .allowlist_var("Priority_.*") + .allowlist_var("UnsignedTransactionStatus_.*") + .allowlist_var("TransactionInfoDirection_.*") + .allowlist_var("AddressBookErrorCode.*") + .allowlist_var("WalletDevice_.*") + .allowlist_var("WalletStatus_.*") + .allowlist_var("WalletConnectionStatus_.*") + .allowlist_var("WalletBackgroundSync_.*") + .allowlist_var("BackgroundSync_.*") + .allowlist_var("LogLevel_.*") + .blocklist_type("__.*") + .blocklist_type("_.*") + .blocklist_function("__.*") + .layout_tests(false) + .default_enum_style(EnumVariation::Rust { + non_exhaustive: false, + }) + .derive_default(false) + .conservative_inline_namespaces() + .generate_comments(false) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("src") + .join("bindings.rs"); + bindings + .write_to_file(out_path.clone()) + .expect("Couldn't write bindings!"); + + // Annotate the generated bindings to ignore certain warnings. + if out_path.exists() { + let contents = fs::read_to_string(out_path.clone()).expect("Failed to read bindings.rs"); + + let prepend_content = "#![allow(non_upper_case_globals)]\n#![allow(dead_code)]\n"; + + if !contents.contains("#![allow(non_upper_case_globals)]") { + let new_contents = format!("{}{}", prepend_content, contents); + + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .open(out_path.clone()) + .expect("Failed to open bindings.rs"); + + file.write_all(new_contents.as_bytes()) + .expect("Failed to write to bindings.rs"); + } + } +} diff --git a/impls/monero.rs/example/Cargo.lock b/impls/monero.rs/example/Cargo.lock new file mode 100644 index 00000000..03e11062 --- /dev/null +++ b/impls/monero.rs/example/Cargo.lock @@ -0,0 +1,444 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "monero_c_rust" +version = "0.0.1" +dependencies = [ + "bindgen", + "mockall", + "tempfile", +] + +[[package]] +name = "monero_example" +version = "0.0.1" +dependencies = [ + "monero_c_rust", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/impls/monero.rs/example/Cargo.toml b/impls/monero.rs/example/Cargo.toml new file mode 100644 index 00000000..a5c30be2 --- /dev/null +++ b/impls/monero.rs/example/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "monero_example" +version = "0.0.1" +edition = "2021" + +[dependencies] +monero_c_rust = { path = ".." } +tempfile = "3.13.0" +# monero_c_rust = { git = "https://github.com/MrCyjaneK/monero_c" } diff --git a/impls/monero.rs/example/README.md b/impls/monero.rs/example/README.md new file mode 100644 index 00000000..febb9cf8 --- /dev/null +++ b/impls/monero.rs/example/README.md @@ -0,0 +1,22 @@ +# `monero_c/impls/monero.rs/example` +Refer to https://github.com/MrCyjaneK/monero_c/blob/master/README.md and +https://github.com/MrCyjaneK/monero_c/blob/master/impls/monero.rs/README.md for +the latest documentation. + +## `monero_c` library +A `monero_c` library is required to build this example. Build or download the +`monero_c` library for your architecture. Follow the upstream docs at +https://github.com/MrCyjaneK/monero_c or download the latest release from +https://github.com/MrCyjaneK/monero_c/releases. The library should then be +placed adjacent to the binary: that is, if your binary is in `target/debug` or +`target/release`, the library should also be in `release` or `debug`, +respectively. Its name should also match your platform as in: +- Android: `libmonero_libwallet2_api_c.so` +- iOS: `MoneroWallet.framework/MoneroWallet` +- Linux: `monero_libwallet2_api_c.so` +- macOS: `monero_libwallet2_api_c.dylib` +- Windows: `monero_libwallet2_api_c.dll` + +On Linux you must also symlink "libmonero_libwallet2_api_c.so" and +"libwallet2_api_c.so" to "monero_libwallet2_api_c.so". Other systems will also +need similar symlinks or copies. diff --git a/impls/monero.rs/example/src/main.rs b/impls/monero.rs/example/src/main.rs new file mode 100644 index 00000000..2de8d2bc --- /dev/null +++ b/impls/monero.rs/example/src/main.rs @@ -0,0 +1,73 @@ +use monero_c_rust::{NetworkType, WalletConfig, WalletError, WalletManager}; +use tempfile::TempDir; + +fn main() -> Result<(), WalletError> { + let manager = WalletManager::new()?; + + let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().unwrap(); + + let wallet = manager.restore_polyseed( + wallet_str.to_string(), + "password".to_string(), + "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(), + NetworkType::Mainnet, + 3, // Restore from the beginning of the blockchain. + 1, // Default KDF rounds. + "".to_string(), // No seed offset. + true, // Create a new wallet. + )?; + + println!("Wallet created successfully."); + + // Print the primary address. + println!("Primary address: {}", wallet.get_address(0, 0)?); + + // Initialize the wallet. + let config = WalletConfig { + daemon_address: "xmr-node.cakewallet.com:18081".to_string(), + upper_transaction_size_limit: 0, // TODO: use sane value. + daemon_username: "".to_string(), + daemon_password: "".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + }; + + // Set WalletManager's daemon address + manager.set_daemon_address(&config.daemon_address); + + // Perform the initialization. + wallet.init(config)?; + wallet.throw_if_error()?; + + // Refresh the wallet. + wallet.refresh()?; + // wallet.refresh_async()?; + wallet.throw_if_error()?; + + // Wait for the refresh to complete. + loop { + let height = manager.get_height()?; + println!("Current blockchain height: {}", height); + if height > 3263501 { + // After this height we can get_balance. + break; + } + // Wait one second. + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + // Get the balance. + let balance_result = wallet.get_balance(0); // Account index 0. + let balance = balance_result.unwrap(); + println!("Balance: {:?}", balance); + + // Clean up the wallet. + std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + std::fs::remove_file(format!("{}.keys", wallet_str)) + .expect("Failed to delete test wallet keys"); + + Ok(()) +} diff --git a/impls/monero.rs/scripts/build_monero_c.sh b/impls/monero.rs/scripts/build_monero_c.sh new file mode 100755 index 00000000..ca9fba40 --- /dev/null +++ b/impls/monero.rs/scripts/build_monero_c.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +set -x -e + +# See https://github.com/MrCyjaneK/monero_c for the most up-to-date build docs, +# this is an example and a starting point for building monero_c for use in Rust +# but it should be automated either using CMake or Cargo (preferred). + +# Detect architecture. +ARCH=$(uname -m) +OS=$(uname -s) + +case $ARCH-$OS in + x86_64-Linux) + TARGET_ARCH="x86_64-linux-gnu" + ;; + i686-Linux) + TARGET_ARCH="i686-linux-gnu" + ;; + aarch64-Linux) + TARGET_ARCH="aarch64-linux-gnu" + ;; + x86_64-Android) + TARGET_ARCH="x86_64-linux-android" + ;; + i686-Android) + TARGET_ARCH="i686-linux-android" + ;; + aarch64-Android) + TARGET_ARCH="aarch64-linux-android" + ;; + armv7l-Android) + TARGET_ARCH="arm-linux-androideabi" + ;; + i686-Windows) + TARGET_ARCH="i686-w64-mingw32" + ;; + x86_64-Windows) + TARGET_ARCH="x86_64-w64-mingw32" + ;; + x86_64-Darwin) + TARGET_ARCH="host-apple-darwin" + ;; + arm64-Darwin) + TARGET_ARCH="host-apple-ios" + ;; + *) + echo "Unsupported architecture: $ARCH on OS: $OS" + exit 1 + ;; +esac + + +unxz -f release/monero/${TARGET_ARCH}_libwallet2_api_c.so.xz +#unxz -f release/wownero/${TARGET_ARCH}_libwallet2_api_c.so.xz + +# Navigate back to /scripts. +cd .. + +# Copy the built .so file to a generic name. +SO_FILE="monero_c/release/monero/${TARGET_ARCH}_libwallet2_api_c.so" +if [[ -f "$SO_FILE" ]]; then + cp "$SO_FILE" "../lib/libwallet2_api_c.so" + echo "Copied $SO_FILE to libwallet2_api_c.so" +else + echo "Error: $SO_FILE not found." + exit 1 +fi diff --git a/impls/monero.rs/src/bindings.rs b/impls/monero.rs/src/bindings.rs new file mode 100644 index 00000000..ebd0d47a --- /dev/null +++ b/impls/monero.rs/src/bindings.rs @@ -0,0 +1,1621 @@ +#![allow(non_upper_case_globals)] +#![allow(dead_code)] +/* automatically generated by rust-bindgen 0.70.1 */ + +pub const MONERO_wallet2_api_c_h_sha256: &[u8; 65] = + b"6c1ba9b57cb185c6dad030b15bcffe8a4772f33930e7f1d62d23b33514ba6f62\0"; +pub const MONERO_wallet2_api_c_cpp_sha256 : & [u8 ; 106] = b"1d1deff340408541f5755b4838d06345f63dcdfffe26b14dbdce32a5de839c55-b089f9ee69924882c5d14dd1a6991deb05d9d1cd\0" ; +pub const MONERO_wallet2_api_c_exp_sha256: &[u8; 65] = + b"d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d\0"; +pub const NetworkType_MAINNET: ::std::os::raw::c_int = 0; +pub const NetworkType_TESTNET: ::std::os::raw::c_int = 1; +pub const NetworkType_STAGENET: ::std::os::raw::c_int = 2; +pub const PendingTransactionStatus_Ok: ::std::os::raw::c_int = 0; +pub const PendingTransactionStatus_Error: ::std::os::raw::c_int = 1; +pub const PendingTransactionStatus_Critical: ::std::os::raw::c_int = 2; +pub const Priority_Default: ::std::os::raw::c_int = 0; +pub const Priority_Low: ::std::os::raw::c_int = 1; +pub const Priority_Medium: ::std::os::raw::c_int = 2; +pub const Priority_High: ::std::os::raw::c_int = 3; +pub const Priority_Last: ::std::os::raw::c_int = 4; +extern "C" { + pub fn MONERO_PendingTransaction_status( + pendingTx_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_PendingTransaction_errorString( + pendingTx_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_commit( + pendingTx_ptr: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + overwrite: bool, + ) -> bool; +} +extern "C" { + pub fn MONERO_PendingTransaction_commitUR( + pendingTx_ptr: *mut ::std::os::raw::c_void, + max_fragment_length: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_amount(pendingTx_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_PendingTransaction_dust(pendingTx_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_PendingTransaction_fee(pendingTx_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_PendingTransaction_txid( + pendingTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_txCount(pendingTx_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_PendingTransaction_subaddrAccount( + pendingTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_subaddrIndices( + pendingTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_multisigSignData( + pendingTx_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_signMultisigTx(pendingTx_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_PendingTransaction_signersKeys( + pendingTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_PendingTransaction_hex( + pendingTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +pub const UnsignedTransactionStatus_Ok: ::std::os::raw::c_int = 0; +pub const UnsignedTransactionStatus_Error: ::std::os::raw::c_int = 1; +pub const UnsignedTransactionStatus_Critical: ::std::os::raw::c_int = 2; +extern "C" { + pub fn MONERO_UnsignedTransaction_status( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_errorString( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_amount( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_fee( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_mixin( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_confirmationMessage( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_paymentId( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_recipientAddress( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_minMixinCount( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + ) -> u64; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_txCount(unsignedTx_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_sign( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + signedFileName: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_UnsignedTransaction_signUR( + unsignedTx_ptr: *mut ::std::os::raw::c_void, + max_fragment_length: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +pub const TransactionInfoDirection_In: ::std::os::raw::c_int = 0; +pub const TransactionInfoDirection_Out: ::std::os::raw::c_int = 1; +extern "C" { + pub fn MONERO_TransactionInfo_direction( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_TransactionInfo_isPending(txInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_TransactionInfo_isFailed(txInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_TransactionInfo_isCoinbase(txInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_TransactionInfo_amount(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_fee(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_blockHeight(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_description( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionInfo_subaddrIndex( + txInfo_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionInfo_subaddrAccount(txInfo_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_TransactionInfo_label( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionInfo_confirmations(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_unlockTime(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_hash( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionInfo_timestamp(txInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_paymentId( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionInfo_transfers_count( + txInfo_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_TransactionInfo_transfers_amount( + txInfo_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> u64; +} +extern "C" { + pub fn MONERO_TransactionInfo_transfers_address( + txInfo_ptr: *mut ::std::os::raw::c_void, + address: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_TransactionHistory_count( + txHistory_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_TransactionHistory_transaction( + txHistory_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_TransactionHistory_transactionById( + txHistory_ptr: *mut ::std::os::raw::c_void, + id: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_TransactionHistory_refresh(txHistory_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_TransactionHistory_setTxNote( + txHistory_ptr: *mut ::std::os::raw::c_void, + txid: *const ::std::os::raw::c_char, + note: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_AddressBookRow_extra( + addressBookRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_AddressBookRow_getAddress( + addressBookRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_AddressBookRow_getDescription( + addressBookRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_AddressBookRow_getPaymentId( + addressBookRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_AddressBookRow_getRowId(addressBookRow_ptr: *mut ::std::os::raw::c_void) + -> usize; +} +pub const AddressBookErrorCodeStatus_Ok: ::std::os::raw::c_int = 0; +pub const AddressBookErrorCodeGeneral_Error: ::std::os::raw::c_int = 1; +pub const AddressBookErrorCodeInvalid_Address: ::std::os::raw::c_int = 2; +pub const AddressBookErrorCodeInvalidPaymentId: ::std::os::raw::c_int = 3; +extern "C" { + pub fn MONERO_AddressBook_getAll_size( + addressBook_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_AddressBook_getAll_byIndex( + addressBook_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_AddressBook_addRow( + addressBook_ptr: *mut ::std::os::raw::c_void, + dst_addr: *const ::std::os::raw::c_char, + payment_id: *const ::std::os::raw::c_char, + description: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_AddressBook_deleteRow( + addressBook_ptr: *mut ::std::os::raw::c_void, + rowId: usize, + ) -> bool; +} +extern "C" { + pub fn MONERO_AddressBook_setDescription( + addressBook_ptr: *mut ::std::os::raw::c_void, + rowId: usize, + description: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_AddressBook_refresh(addressBook_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_AddressBook_errorString( + addressBook_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_AddressBook_errorCode( + addressBook_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_AddressBook_lookupPaymentID( + addressBook_ptr: *mut ::std::os::raw::c_void, + payment_id: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_CoinsInfo_blockHeight(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_CoinsInfo_hash( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_CoinsInfo_internalOutputIndex( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> usize; +} +extern "C" { + pub fn MONERO_CoinsInfo_globalOutputIndex(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_CoinsInfo_spent(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_frozen(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_spentHeight(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_CoinsInfo_amount(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_CoinsInfo_rct(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_keyImageKnown(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_pkIndex(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> usize; +} +extern "C" { + pub fn MONERO_CoinsInfo_subaddrIndex(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_CoinsInfo_subaddrAccount(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_CoinsInfo_address( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_CoinsInfo_addressLabel( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_CoinsInfo_keyImage( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_CoinsInfo_unlockTime(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_CoinsInfo_unlocked(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_pubKey( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_CoinsInfo_coinbase(coinsInfo_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_CoinsInfo_description( + coinsInfo_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Coins_count(coins_ptr: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Coins_coin( + coins_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Coins_getAll_size( + coins_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Coins_getAll_byIndex( + coins_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Coins_refresh(coins_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Coins_setFrozenByPublicKey( + coins_ptr: *mut ::std::os::raw::c_void, + public_key: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Coins_setFrozen( + coins_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn MONERO_Coins_thaw(coins_ptr: *mut ::std::os::raw::c_void, index: ::std::os::raw::c_int); +} +extern "C" { + pub fn MONERO_Coins_thawByPublicKey( + coins_ptr: *mut ::std::os::raw::c_void, + public_key: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Coins_isTransferUnlocked( + coins_ptr: *mut ::std::os::raw::c_void, + unlockTime: u64, + blockHeight: u64, + ) -> bool; +} +extern "C" { + pub fn MONERO_Coins_setDescription( + coins_ptr: *mut ::std::os::raw::c_void, + public_key: *const ::std::os::raw::c_char, + description: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_SubaddressRow_extra( + subaddressRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressRow_getAddress( + subaddressRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressRow_getLabel( + subaddressRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressRow_getRowId(subaddressRow_ptr: *mut ::std::os::raw::c_void) -> usize; +} +extern "C" { + pub fn MONERO_Subaddress_getAll_size( + subaddress_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Subaddress_getAll_byIndex( + subaddress_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Subaddress_addRow( + subaddress_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Subaddress_setLabel( + subaddress_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + addressIndex: u32, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Subaddress_refresh( + subaddress_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + ); +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_extra( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_getAddress( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_getLabel( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_getBalance( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_getUnlockedBalance( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_SubaddressAccountRow_getRowId( + subaddressAccountRow_ptr: *mut ::std::os::raw::c_void, + ) -> usize; +} +extern "C" { + pub fn MONERO_SubaddressAccount_getAll_size( + subaddressAccount_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_SubaddressAccount_getAll_byIndex( + subaddressAccount_ptr: *mut ::std::os::raw::c_void, + index: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_SubaddressAccount_addRow( + subaddressAccount_ptr: *mut ::std::os::raw::c_void, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_SubaddressAccount_setLabel( + subaddressAccount_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_SubaddressAccount_refresh(subaddressAccount_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_MultisigState_isMultisig(multisigState_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_MultisigState_isReady(multisigState_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_MultisigState_threshold(multisigState_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_MultisigState_total(multisigState_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_DeviceProgress_progress(deviceProgress_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_DeviceProgress_indeterminate( + deviceProgress_ptr: *mut ::std::os::raw::c_void, + ) -> bool; +} +pub const WalletDevice_Software: ::std::os::raw::c_int = 0; +pub const WalletDevice_Ledger: ::std::os::raw::c_int = 1; +pub const WalletDevice_Trezor: ::std::os::raw::c_int = 2; +pub const WalletStatus_Ok: ::std::os::raw::c_int = 0; +pub const WalletStatus_Error: ::std::os::raw::c_int = 1; +pub const WalletStatus_Critical: ::std::os::raw::c_int = 2; +pub const WalletConnectionStatus_Disconnected: ::std::os::raw::c_int = 0; +pub const WalletConnectionStatus_Connected: ::std::os::raw::c_int = 1; +pub const WalletConnectionStatus_WrongVersion: ::std::os::raw::c_int = 2; +pub const WalletBackgroundSync_Off: ::std::os::raw::c_int = 0; +pub const WalletBackgroundSync_ReusePassword: ::std::os::raw::c_int = 1; +pub const BackgroundSync_CustomPassword: ::std::os::raw::c_int = 2; +extern "C" { + pub fn MONERO_Wallet_seed( + wallet_ptr: *mut ::std::os::raw::c_void, + seed_offset: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_getSeedLanguage( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_setSeedLanguage( + wallet_ptr: *mut ::std::os::raw::c_void, + arg: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Wallet_status(wallet_ptr: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_errorString( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_setPassword( + wallet_ptr: *mut ::std::os::raw::c_void, + password: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getPassword( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_setDevicePin( + wallet_ptr: *mut ::std::os::raw::c_void, + pin: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setDevicePassphrase( + wallet_ptr: *mut ::std::os::raw::c_void, + passphrase: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_address( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u64, + addressIndex: u64, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_path( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_nettype(wallet_ptr: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_useForkRules( + wallet_ptr: *mut ::std::os::raw::c_void, + version: u8, + early_blocks: i64, + ) -> u8; +} +extern "C" { + pub fn MONERO_Wallet_integratedAddress( + wallet_ptr: *mut ::std::os::raw::c_void, + payment_id: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_secretViewKey( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_publicViewKey( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_secretSpendKey( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_publicSpendKey( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_publicMultisigSignerKey( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_stop(wallet_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Wallet_store( + wallet_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_filename( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_keysFilename( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_init( + wallet_ptr: *mut ::std::os::raw::c_void, + daemon_address: *const ::std::os::raw::c_char, + upper_transaction_size_limit: u64, + daemon_username: *const ::std::os::raw::c_char, + daemon_password: *const ::std::os::raw::c_char, + use_ssl: bool, + lightWallet: bool, + proxy_address: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_createWatchOnly( + wallet_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + language: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setRefreshFromBlockHeight( + wallet_ptr: *mut ::std::os::raw::c_void, + refresh_from_block_height: u64, + ); +} +extern "C" { + pub fn MONERO_Wallet_getRefreshFromBlockHeight(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_setRecoveringFromSeed( + wallet_ptr: *mut ::std::os::raw::c_void, + recoveringFromSeed: bool, + ); +} +extern "C" { + pub fn MONERO_Wallet_setRecoveringFromDevice( + wallet_ptr: *mut ::std::os::raw::c_void, + recoveringFromDevice: bool, + ); +} +extern "C" { + pub fn MONERO_Wallet_setSubaddressLookahead( + wallet_ptr: *mut ::std::os::raw::c_void, + major: u32, + minor: u32, + ); +} +extern "C" { + pub fn MONERO_Wallet_connectToDaemon(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_connected( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_setTrustedDaemon(wallet_ptr: *mut ::std::os::raw::c_void, arg: bool); +} +extern "C" { + pub fn MONERO_Wallet_trustedDaemon(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setProxy( + wallet_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_balance(wallet_ptr: *mut ::std::os::raw::c_void, accountIndex: u32) + -> u64; +} +extern "C" { + pub fn MONERO_Wallet_unlockedBalance( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + ) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_viewOnlyBalance( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + ) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_watchOnly(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_isDeterministic(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_blockChainHeight(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_approximateBlockChainHeight( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_estimateBlockChainHeight(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_daemonBlockChainHeight(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_daemonBlockChainTargetHeight( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_synchronized(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_displayAmount(amount: u64) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_amountFromString(amount: *const ::std::os::raw::c_char) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_amountFromDouble(amount: f64) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_genPaymentId() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_paymentIdValid(paiment_id: *const ::std::os::raw::c_char) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_addressValid( + str_: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_keyValid( + secret_key_string: *const ::std::os::raw::c_char, + address_string: *const ::std::os::raw::c_char, + isViewKey: bool, + nettype: ::std::os::raw::c_int, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_keyValid_error( + secret_key_string: *const ::std::os::raw::c_char, + address_string: *const ::std::os::raw::c_char, + isViewKey: bool, + nettype: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_paymentIdFromAddress( + strarg: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_maximumAllowedAmount() -> u64; +} +extern "C" { + pub fn MONERO_Wallet_init3( + wallet_ptr: *mut ::std::os::raw::c_void, + argv0: *const ::std::os::raw::c_char, + default_log_base_name: *const ::std::os::raw::c_char, + log_path: *const ::std::os::raw::c_char, + console: bool, + ); +} +extern "C" { + pub fn MONERO_Wallet_getPolyseed( + wallet_ptr: *mut ::std::os::raw::c_void, + passphrase: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_createPolyseed( + language: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_startRefresh(wallet_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Wallet_pauseRefresh(wallet_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Wallet_refresh(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_refreshAsync(wallet_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Wallet_rescanBlockchain(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_rescanBlockchainAsync(wallet_ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_Wallet_setAutoRefreshInterval( + wallet_ptr: *mut ::std::os::raw::c_void, + millis: ::std::os::raw::c_int, + ); +} +extern "C" { + pub fn MONERO_Wallet_autoRefreshInterval( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_addSubaddressAccount( + wallet_ptr: *mut ::std::os::raw::c_void, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Wallet_numSubaddressAccounts(wallet_ptr: *mut ::std::os::raw::c_void) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_numSubaddresses( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + ) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_addSubaddress( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Wallet_getSubaddressLabel( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + addressIndex: u32, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_setSubaddressLabel( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + addressIndex: u32, + label: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_Wallet_multisig( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_getMultisigInfo( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_makeMultisig( + wallet_ptr: *mut ::std::os::raw::c_void, + info: *const ::std::os::raw::c_char, + info_separator: *const ::std::os::raw::c_char, + threshold: u32, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_exchangeMultisigKeys( + wallet_ptr: *mut ::std::os::raw::c_void, + info: *const ::std::os::raw::c_char, + info_separator: *const ::std::os::raw::c_char, + force_update_use_with_caution: bool, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_exportMultisigImages( + wallet_ptr: *mut ::std::os::raw::c_void, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_importMultisigImages( + wallet_ptr: *mut ::std::os::raw::c_void, + info: *const ::std::os::raw::c_char, + info_separator: *const ::std::os::raw::c_char, + ) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_hasMultisigPartialKeyImages( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_restoreMultisigTransaction( + wallet_ptr: *mut ::std::os::raw::c_void, + signData: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_createTransactionMultDest( + wallet_ptr: *mut ::std::os::raw::c_void, + dst_addr_list: *const ::std::os::raw::c_char, + dst_addr_list_separator: *const ::std::os::raw::c_char, + payment_id: *const ::std::os::raw::c_char, + amount_sweep_all: bool, + amount_list: *const ::std::os::raw::c_char, + amount_list_separator: *const ::std::os::raw::c_char, + mixin_count: u32, + pendingTransactionPriority: ::std::os::raw::c_int, + subaddr_account: u32, + preferredInputs: *const ::std::os::raw::c_char, + preferredInputs_separator: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_createTransaction( + wallet_ptr: *mut ::std::os::raw::c_void, + dst_addr: *const ::std::os::raw::c_char, + payment_id: *const ::std::os::raw::c_char, + amount: u64, + mixin_count: u32, + pendingTransactionPriority: ::std::os::raw::c_int, + subaddr_account: u32, + preferredInputs: *const ::std::os::raw::c_char, + separator: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_loadUnsignedTx( + wallet_ptr: *mut ::std::os::raw::c_void, + unsigned_filename: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_loadUnsignedTxUR( + wallet_ptr: *mut ::std::os::raw::c_void, + input: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_submitTransaction( + wallet_ptr: *mut ::std::os::raw::c_void, + fileName: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_submitTransactionUR( + wallet_ptr: *mut ::std::os::raw::c_void, + input: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_hasUnknownKeyImages(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_exportKeyImages( + wallet_ptr: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + all: bool, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_exportKeyImagesUR( + wallet_ptr: *mut ::std::os::raw::c_void, + max_fragment_length: usize, + all: bool, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_importKeyImages( + wallet_ptr: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_importKeyImagesUR( + wallet_ptr: *mut ::std::os::raw::c_void, + input: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_exportOutputs( + wallet_ptr: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + all: bool, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_exportOutputsUR( + wallet_ptr: *mut ::std::os::raw::c_void, + max_fragment_length: usize, + all: bool, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_importOutputs( + wallet_ptr: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_importOutputsUR( + wallet_ptr: *mut ::std::os::raw::c_void, + input: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setupBackgroundSync( + wallet_ptr: *mut ::std::os::raw::c_void, + background_sync_type: ::std::os::raw::c_int, + wallet_password: *const ::std::os::raw::c_char, + background_cache_password: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getBackgroundSyncType( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_startBackgroundSync(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_stopBackgroundSync( + wallet_ptr: *mut ::std::os::raw::c_void, + wallet_password: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_isBackgroundSyncing(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_isBackgroundWallet(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_history( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_addressBook( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_coins( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_subaddress( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_subaddressAccount( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_Wallet_defaultMixin(wallet_ptr: *mut ::std::os::raw::c_void) -> u32; +} +extern "C" { + pub fn MONERO_Wallet_setDefaultMixin(wallet_ptr: *mut ::std::os::raw::c_void, arg: u32); +} +extern "C" { + pub fn MONERO_Wallet_setCacheAttribute( + wallet_ptr: *mut ::std::os::raw::c_void, + key: *const ::std::os::raw::c_char, + val: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getCacheAttribute( + wallet_ptr: *mut ::std::os::raw::c_void, + key: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_setUserNote( + wallet_ptr: *mut ::std::os::raw::c_void, + txid: *const ::std::os::raw::c_char, + note: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getUserNote( + wallet_ptr: *mut ::std::os::raw::c_void, + txid: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_getTxKey( + wallet_ptr: *mut ::std::os::raw::c_void, + txid: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_checkTxKey( + wallet_ptr: *mut ::std::os::raw::c_void, + txid: *const ::std::os::raw::c_char, + tx_key: *const ::std::os::raw::c_char, + address: *const ::std::os::raw::c_char, + received: u64, + in_pool: bool, + confirmations: u64, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_signMessage( + wallet_ptr: *mut ::std::os::raw::c_void, + message: *const ::std::os::raw::c_char, + address: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_verifySignedMessage( + wallet_ptr: *mut ::std::os::raw::c_void, + message: *const ::std::os::raw::c_char, + address: *const ::std::os::raw::c_char, + signature: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_rescanSpent(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setOffline(wallet_ptr: *mut ::std::os::raw::c_void, offline: bool); +} +extern "C" { + pub fn MONERO_Wallet_isOffline(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_segregatePreForkOutputs( + wallet_ptr: *mut ::std::os::raw::c_void, + segregate: bool, + ); +} +extern "C" { + pub fn MONERO_Wallet_segregationHeight(wallet_ptr: *mut ::std::os::raw::c_void, height: u64); +} +extern "C" { + pub fn MONERO_Wallet_keyReuseMitigation2( + wallet_ptr: *mut ::std::os::raw::c_void, + mitigation: bool, + ); +} +extern "C" { + pub fn MONERO_Wallet_lockKeysFile(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_unlockKeysFile(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_isKeysFileLocked(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getDeviceType( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_Wallet_coldKeyImageSync( + wallet_ptr: *mut ::std::os::raw::c_void, + spent: u64, + unspent: u64, + ) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_deviceShowAddress( + wallet_ptr: *mut ::std::os::raw::c_void, + accountIndex: u32, + addressIndex: u32, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_Wallet_reconnectDevice(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getBytesReceived(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_getBytesSent(wallet_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_Wallet_getStateIsConnected(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getSendToDevice( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { + pub fn MONERO_Wallet_getSendToDeviceLength(wallet_ptr: *mut ::std::os::raw::c_void) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_getReceivedFromDevice( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_uchar; +} +extern "C" { + pub fn MONERO_Wallet_getReceivedFromDeviceLength( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> usize; +} +extern "C" { + pub fn MONERO_Wallet_getWaitsForDeviceSend(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_getWaitsForDeviceReceive(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_Wallet_setDeviceReceivedData( + wallet_ptr: *mut ::std::os::raw::c_void, + data: *mut ::std::os::raw::c_uchar, + len: usize, + ); +} +extern "C" { + pub fn MONERO_Wallet_setDeviceSendData( + wallet_ptr: *mut ::std::os::raw::c_void, + data: *mut ::std::os::raw::c_uchar, + len: usize, + ); +} +extern "C" { + pub fn MONERO_WalletManager_createWallet( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + language: *const ::std::os::raw::c_char, + networkType: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_openWallet( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + networkType: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_recoveryWallet( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + mnemonic: *const ::std::os::raw::c_char, + networkType: ::std::os::raw::c_int, + restoreHeight: u64, + kdfRounds: u64, + seedOffset: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_createWalletFromKeys( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + language: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + restoreHeight: u64, + addressString: *const ::std::os::raw::c_char, + viewKeyString: *const ::std::os::raw::c_char, + spendKeyString: *const ::std::os::raw::c_char, + kdf_rounds: u64, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_createDeterministicWalletFromSpendKey( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + language: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + restoreHeight: u64, + spendKeyString: *const ::std::os::raw::c_char, + kdf_rounds: u64, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_createWalletFromDevice( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + deviceName: *const ::std::os::raw::c_char, + restoreHeight: u64, + subaddressLookahead: *const ::std::os::raw::c_char, + viewKeyString: *const ::std::os::raw::c_char, + spendKeyString: *const ::std::os::raw::c_char, + kdf_rounds: u64, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_createWalletFromPolyseed( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + nettype: ::std::os::raw::c_int, + mnemonic: *const ::std::os::raw::c_char, + passphrase: *const ::std::os::raw::c_char, + newWallet: bool, + restore_height: u64, + kdf_rounds: u64, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManager_closeWallet( + wm_ptr: *mut ::std::os::raw::c_void, + wallet_ptr: *mut ::std::os::raw::c_void, + store: bool, + ) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_walletExists( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_verifyWalletPassword( + wm_ptr: *mut ::std::os::raw::c_void, + keys_file_name: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + no_spend_key: bool, + kdf_rounds: u64, + ) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_queryWalletDevice( + wm_ptr: *mut ::std::os::raw::c_void, + keys_file_name: *const ::std::os::raw::c_char, + password: *const ::std::os::raw::c_char, + kdf_rounds: u64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_WalletManager_findWallets( + wm_ptr: *mut ::std::os::raw::c_void, + path: *const ::std::os::raw::c_char, + separator: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_WalletManager_errorString( + wm_ptr: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_WalletManager_setDaemonAddress( + wm_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn MONERO_WalletManager_blockchainHeight(wm_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_WalletManager_blockchainTargetHeight(wm_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_WalletManager_networkDifficulty(wm_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_WalletManager_miningHashRate(wm_ptr: *mut ::std::os::raw::c_void) -> f64; +} +extern "C" { + pub fn MONERO_WalletManager_blockTarget(wm_ptr: *mut ::std::os::raw::c_void) -> u64; +} +extern "C" { + pub fn MONERO_WalletManager_isMining(wm_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_startMining( + wm_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + threads: u32, + backgroundMining: bool, + ignoreBattery: bool, + ) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_stopMining( + wm_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn MONERO_WalletManager_resolveOpenAlias( + wm_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + dnssec_valid: bool, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_WalletManager_setProxy( + wm_ptr: *mut ::std::os::raw::c_void, + address: *const ::std::os::raw::c_char, + ) -> bool; +} +pub const LogLevel_Silent: ::std::os::raw::c_int = -1; +pub const LogLevel_0: ::std::os::raw::c_int = 0; +pub const LogLevel_1: ::std::os::raw::c_int = 1; +pub const LogLevel_2: ::std::os::raw::c_int = 2; +pub const LogLevel_3: ::std::os::raw::c_int = 3; +pub const LogLevel_4: ::std::os::raw::c_int = 4; +pub const LogLevel_Min: ::std::os::raw::c_int = -1; +pub const LogLevel_Max: ::std::os::raw::c_int = 4; +extern "C" { + pub fn MONERO_WalletManagerFactory_getWalletManager() -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_WalletManagerFactory_setLogLevel(level: ::std::os::raw::c_int); +} +extern "C" { + pub fn MONERO_WalletManagerFactory_setLogCategories(categories: *const ::std::os::raw::c_char); +} +extern "C" { + pub fn MONERO_DEBUG_test0(); +} +extern "C" { + pub fn MONERO_DEBUG_test1(x: bool) -> bool; +} +extern "C" { + pub fn MONERO_DEBUG_test2(x: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn MONERO_DEBUG_test3(x: u64) -> u64; +} +extern "C" { + pub fn MONERO_DEBUG_test4(x: u64) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_DEBUG_test5() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_DEBUG_test5_std() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_DEBUG_isPointerNull(wallet_ptr: *mut ::std::os::raw::c_void) -> bool; +} +extern "C" { + pub fn MONERO_cw_getWalletListener( + wallet_ptr: *mut ::std::os::raw::c_void, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn MONERO_cw_WalletListener_resetNeedToRefresh( + cw_walletListener_ptr: *mut ::std::os::raw::c_void, + ); +} +extern "C" { + pub fn MONERO_cw_WalletListener_isNeedToRefresh( + cw_walletListener_ptr: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MONERO_cw_WalletListener_isNewTransactionExist( + cw_walletListener_ptr: *mut ::std::os::raw::c_void, + ) -> bool; +} +extern "C" { + pub fn MONERO_cw_WalletListener_resetIsNewTransactionExist( + cw_walletListener_ptr: *mut ::std::os::raw::c_void, + ); +} +extern "C" { + pub fn MONERO_cw_WalletListener_height( + cw_walletListener_ptr: *mut ::std::os::raw::c_void, + ) -> u64; +} +extern "C" { + pub fn MONERO_free(ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn MONERO_checksum_wallet2_api_c_h() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_checksum_wallet2_api_c_cpp() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn MONERO_checksum_wallet2_api_c_exp() -> *const ::std::os::raw::c_char; +} diff --git a/impls/monero.rs/src/lib.rs b/impls/monero.rs/src/lib.rs new file mode 100644 index 00000000..d8167bb1 --- /dev/null +++ b/impls/monero.rs/src/lib.rs @@ -0,0 +1,2270 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::{c_int, c_void}; +use std::ptr::NonNull; +use std::sync::Arc; + +pub mod bindings; +pub use bindings::WalletStatus_Critical; +pub use bindings::WalletStatus_Error; +pub use bindings::WalletStatus_Ok; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NetworkType { + Mainnet = bindings::NetworkType_MAINNET as isize, + Testnet = bindings::NetworkType_TESTNET as isize, + Stagenet = bindings::NetworkType_STAGENET as isize, +} + +impl NetworkType { + pub fn from_c_int(value: c_int) -> Option { + match value { + bindings::NetworkType_MAINNET => Some(NetworkType::Mainnet), + bindings::NetworkType_TESTNET => Some(NetworkType::Testnet), + bindings::NetworkType_STAGENET => Some(NetworkType::Stagenet), + _ => None, + } + } + + pub fn to_c_int(self) -> c_int { + self as c_int + } +} + +#[derive(Debug)] +pub enum WalletError { + NullPointer, + FfiError(String), + WalletErrorCode(c_int, String), +} + +pub type WalletResult = Result; + +#[derive(Debug)] +pub struct Account { + pub index: u32, + pub label: String, + pub balance: u64, + pub unlocked_balance: u64, +} + +#[derive(Debug)] +pub struct GetAccounts { + pub accounts: Vec, +} + +pub struct Wallet { + pub ptr: NonNull, + pub manager: Arc, + pub is_closed: bool, +} + +pub struct WalletManager { + ptr: NonNull, +} + +/// Configuration parameters for initializing a wallet. +#[derive(Debug, Clone)] +pub struct WalletConfig { + pub daemon_address: String, + pub upper_transaction_size_limit: u64, + pub daemon_username: String, + pub daemon_password: String, + pub use_ssl: bool, + pub light_wallet: bool, + pub proxy_address: String, +} + +impl Default for WalletConfig { + fn default() -> Self { + WalletConfig { + daemon_address: "localhost:18081".to_string(), + upper_transaction_size_limit: 10000, // TODO: set sane value. + daemon_username: "".to_string(), + daemon_password: "".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + } + } +} + +pub type BlockHeight = u64; + +#[derive(Debug)] +pub struct Refreshed; + +/// Represents a destination address and the amount to send. +#[derive(Debug, Clone)] +pub struct Destination { + /// The recipient's address. + pub address: String, + /// The amount to send to the recipient (in atomic units). + pub amount: u64, +} + +/// Represents the result of a transfer operation. +#[derive(Debug)] +pub struct Transfer { + /// The transaction ID of the transfer. + pub txid: String, + /// The transaction key, if requested. + pub tx_key: Option, + /// The total amount sent in the transfer. + pub amount: u64, + /// The fee associated with the transfer. + pub fee: u64, +} + +/// Represents the result of checking a transaction key against a transaction ID and address. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CheckTxKey { + /// Indicates whether the transaction key is valid. + pub valid: bool, + /// An optional error message providing details if the verification fails. + pub error: Option, +} + +impl WalletManager { + /// Creates a new `WalletManager`. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::WalletManager; + /// let manager = WalletManager::new(); + /// assert!(manager.is_ok()); + /// ``` + pub fn new() -> WalletResult> { + unsafe { + bindings::MONERO_WalletManagerFactory_setLogLevel(4); + let ptr = bindings::MONERO_WalletManagerFactory_getWalletManager(); + let ptr = NonNull::new(ptr).ok_or(WalletError::NullPointer)?; + Ok(Arc::new(WalletManager { ptr })) + } + } + + /// Check the status of a wallet to ensure it's in a valid state. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// + /// // Check the status of the wallet, expecting OK + /// let status_result = manager.get_status(wallet.ptr.as_ptr()); + /// assert!(status_result.is_ok(), "Failed to get status: {:?}", status_result.err()); + /// assert_eq!(status_result.unwrap(), (), "Expected status to be OK"); + /// + /// // Clean up wallet files. + /// std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// std::fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn get_status(&self, wallet_ptr: *mut c_void) -> WalletResult<()> { + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); // Ensure NullPointer is returned for null wallet + } + + unsafe { + let status = bindings::MONERO_Wallet_status(wallet_ptr); + + if status == bindings::WalletStatus_Ok { + Ok(()) + } else { + let error_ptr = bindings::MONERO_Wallet_errorString(wallet_ptr); + let error_msg = if error_ptr.is_null() { + "Unknown error".to_string() + } else { + CStr::from_ptr(error_ptr).to_string_lossy().into_owned() + }; + Err(WalletError::WalletErrorCode(status, error_msg)) + } + } + } + + /// Check the status of a wallet and throw an error if an issue is found. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// + /// // Check the status of the wallet, expecting OK + /// let status_result = manager.throw_if_error(wallet.ptr.as_ptr()); + /// assert!(status_result.is_ok(), "Failed to get status: {:?}", status_result.err()); + /// + /// // Clean up wallet files. + /// std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// std::fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn throw_if_error(&self, wallet_ptr: *mut c_void) -> WalletResult<()> { + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + unsafe { + let status = bindings::MONERO_Wallet_status(wallet_ptr); + if status == bindings::WalletStatus_Ok { + Ok(()) + } else { + let error_ptr = bindings::MONERO_Wallet_errorString(wallet_ptr); + let error_msg = if error_ptr.is_null() { + "Unknown error".to_string() + } else { + CStr::from_ptr(error_ptr).to_string_lossy().into_owned() + }; + Err(WalletError::WalletErrorCode(status, error_msg)) + } + } + } + + /// Create a new wallet. + /// + /// Generates a new wallet with the provided path, password, language, and network type, with a + /// new mnemonic seed generated in the specified language. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use std::fs; + /// use std::path::Path; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet.is_ok()); + /// + /// // Clean up wallet files. + /// std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// std::fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn create_wallet( + self: &Arc, + path: &str, + password: &str, + language: &str, + network_type: NetworkType, + ) -> WalletResult { + let c_path = + CString::new(path).map_err(|_| WalletError::FfiError("Invalid path".to_string()))?; + let c_password = CString::new(password) + .map_err(|_| WalletError::FfiError("Invalid password".to_string()))?; + let c_language = CString::new(language) + .map_err(|_| WalletError::FfiError("Invalid language".to_string()))?; + + unsafe { + let wallet_ptr = bindings::MONERO_WalletManager_createWallet( + self.ptr.as_ptr(), + c_path.as_ptr(), + c_password.as_ptr(), + c_language.as_ptr(), + network_type.to_c_int(), + ); + + self.throw_if_error(wallet_ptr)?; + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + Ok(Wallet { + ptr: NonNull::new(wallet_ptr).unwrap(), + manager: Arc::clone(self), + is_closed: false, + }) + } + } + + /// Restores a wallet from a mnemonic seed. + /// + /// This method restores a Monero wallet using the provided mnemonic seed, network type, and other + /// configuration parameters. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("restored_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap().to_string(); + /// + /// let manager = WalletManager::new().expect("Failed to create WalletManager"); + /// + /// let mnemonic = "hemlock jubilee eden hacksaw boil superior inroads epoxy exhale orders cavernous second brunt saved richly lower upgrade hitched launching deepest mostly playful layout lower eden".to_string(); + /// let wallet = manager.restore_mnemonic( + /// wallet_str, + /// "strong_password".to_string(), + /// mnemonic, + /// NetworkType::Mainnet, + /// 0, // Restore from the beginning of the blockchain. + /// 1, // Default KDF rounds. + /// "".to_string(), // No seed offset. + /// ).expect("Failed to restore wallet"); + /// + /// // Use the wallet as needed... + /// + /// // Clean up wallet files. + /// fs::remove_file(&wallet_path).expect("Failed to delete restored wallet"); + /// fs::remove_file(format!("{}.keys", wallet_path.display())).expect("Failed to delete restored wallet keys"); + /// ``` + pub fn restore_mnemonic( + self: &Arc, + path: String, + password: String, + seed: String, + network_type: NetworkType, + restore_height: u64, + kdf_rounds: u64, + seed_offset: String, // TODO: Make an Option. + ) -> WalletResult { + // Convert Rust strings to C-compatible strings. + let c_path = CString::new(path) + .map_err(|_| WalletError::FfiError("Invalid path string".to_string()))?; + let c_password = CString::new(password) + .map_err(|_| WalletError::FfiError("Invalid password string".to_string()))?; + let c_seed = CString::new(seed) + .map_err(|_| WalletError::FfiError("Invalid seed string".to_string()))?; + let c_seed_offset = CString::new(seed_offset) + .map_err(|_| WalletError::FfiError("Invalid seed_offset string".to_string()))?; + + unsafe { + let wallet_ptr = bindings::MONERO_WalletManager_recoveryWallet( + self.ptr.as_ptr(), + c_path.as_ptr(), + c_password.as_ptr(), + c_seed.as_ptr(), + network_type.to_c_int(), + restore_height, + kdf_rounds, + c_seed_offset.as_ptr(), + ); + + // Check for errors using the returned wallet pointer. + self.throw_if_error(wallet_ptr)?; + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + Ok(Wallet { + ptr: NonNull::new(wallet_ptr).unwrap(), + manager: Arc::clone(self), + is_closed: false, + }) + } + } + + /// Restores a wallet from a polyseed. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap().to_string(); + /// + /// let manager = WalletManager::new().expect("Failed to create WalletManager"); + /// + /// let polyseed = "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(); + /// let restored_wallet = manager.restore_polyseed( + /// wallet_str.clone(), + /// "password".to_string(), + /// polyseed, + /// NetworkType::Mainnet, + /// 0, // Restore from the beginning of the blockchain. + /// 1, // Default KDF rounds. + /// "".to_string(), // No seed offset. + /// true, // Create a new wallet. + /// ).expect("Failed to restore wallet from polyseed"); + /// + /// // Use the restored_wallet as needed... + /// + /// // Clean up wallet files. + /// fs::remove_file(&wallet_path).expect("Failed to delete restored wallet"); + /// fs::remove_file(format!("{}.keys", wallet_path.display())).expect("Failed to delete restored wallet keys"); + /// ``` + pub fn restore_polyseed( + self: &Arc, + path: String, + password: String, + polyseed: String, + network_type: NetworkType, + restore_height: u64, + kdf_rounds: u64, + seed_offset: String, + new_wallet: bool, + ) -> WalletResult { + // Convert Rust strings to C-compatible strings. + let c_path = CString::new(path) + .map_err(|_| WalletError::FfiError("Invalid path string".to_string()))?; + let c_password = CString::new(password) + .map_err(|_| WalletError::FfiError("Invalid password string".to_string()))?; + let c_polyseed = CString::new(polyseed) + .map_err(|_| WalletError::FfiError("Invalid mnemonic string".to_string()))?; + let c_seed_offset = CString::new(seed_offset) + .map_err(|_| WalletError::FfiError("Invalid seed offset string".to_string()))?; + + unsafe { + let wallet_ptr = bindings::MONERO_WalletManager_createWalletFromPolyseed( + self.ptr.as_ptr(), + c_path.as_ptr(), + c_password.as_ptr(), + network_type.to_c_int(), + c_polyseed.as_ptr(), + c_seed_offset.as_ptr(), + new_wallet, + restore_height, + kdf_rounds, + ); + + // Check for errors using the returned wallet pointer. + self.throw_if_error(wallet_ptr)?; + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + Ok(Wallet { + ptr: NonNull::new(wallet_ptr).unwrap(), + manager: Arc::clone(self), + is_closed: false, + }) + } + } + + /// Generates a wallet from provided keys. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use std::fs; + /// use std::path::Path; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.generate_from_keys( + /// wallet_str.to_string(), + /// "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi".to_string(), + /// "".to_string(), // Spend key optional: you can pass either the spend key or the view key. + /// "3bc0b202cde92fe5719c3cc0a16aa94f88a5d19f8c515d4e35fae361f6f2120e".to_string(), + /// 0, // Restore from the beginning of the blockchain. + /// "password".to_string(), + /// "English".to_string(), + /// NetworkType::Mainnet, + /// 1, // Default KDF rounds. + /// ); + /// assert!(wallet.is_ok(), "Failed to generate wallet from keys: {:?}", wallet.err()); + /// + /// // Clean up wallet files. + /// std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// std::fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn generate_from_keys( + self: &Arc, + filename: String, + address: String, + spendkey: String, + viewkey: String, + restore_height: u64, + password: String, + language: String, + network_type: NetworkType, + kdf_rounds: u64, + ) -> WalletResult { + let c_filename = CString::new(filename) + .map_err(|_| WalletError::FfiError("Invalid filename".to_string()))?; + let c_password = CString::new(password) + .map_err(|_| WalletError::FfiError("Invalid password".to_string()))?; + let c_language = CString::new(language) + .map_err(|_| WalletError::FfiError("Invalid language".to_string()))?; + let c_address = CString::new(address) + .map_err(|_| WalletError::FfiError("Invalid address".to_string()))?; + let c_spendkey = CString::new(spendkey) + .map_err(|_| WalletError::FfiError("Invalid spendkey".to_string()))?; + let c_viewkey = CString::new(viewkey) + .map_err(|_| WalletError::FfiError("Invalid viewkey".to_string()))?; + + unsafe { + let wallet_ptr = bindings::MONERO_WalletManager_createWalletFromKeys( + self.ptr.as_ptr(), + c_filename.as_ptr(), + c_password.as_ptr(), + c_language.as_ptr(), + network_type.to_c_int(), + restore_height, + c_address.as_ptr(), + c_viewkey.as_ptr(), + c_spendkey.as_ptr(), + kdf_rounds, + ); + + if wallet_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + self.throw_if_error(wallet_ptr)?; + + Ok(Wallet { + ptr: NonNull::new(wallet_ptr).unwrap(), + manager: Arc::clone(self), + is_closed: false, + }) + } + } + + /// Opens an existing wallet with the provided path, password, and network type. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// + /// // First, create a wallet to open later. + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// + /// // Close the wallet by dropping it. + /// drop(wallet); + /// + /// // Now try to open the existing wallet. + /// let open_result = manager.open_wallet(wallet_str, "password", NetworkType::Mainnet); + /// assert!(open_result.is_ok(), "Failed to open wallet: {:?}", open_result.err()); + /// let opened_wallet = open_result.unwrap(); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn open_wallet( + self: &Arc, + path: &str, + password: &str, + network_type: NetworkType, + ) -> WalletResult { + let c_path = + CString::new(path).map_err(|_| WalletError::FfiError("Invalid path".to_string()))?; + let c_password = CString::new(password) + .map_err(|_| WalletError::FfiError("Invalid password".to_string()))?; + + unsafe { + let wallet_ptr = bindings::MONERO_WalletManager_openWallet( + self.ptr.as_ptr(), + c_path.as_ptr(), + c_password.as_ptr(), + network_type.to_c_int(), + ); + + self.throw_if_error(wallet_ptr)?; + if wallet_ptr.is_null() { + Err(self.get_status(wallet_ptr).unwrap_err()) + } else { + // Ensuring that we properly close the wallet when it's no longer needed + let wallet = Wallet { + ptr: NonNull::new(wallet_ptr).unwrap(), + manager: Arc::clone(self), + is_closed: false, + }; + Ok(wallet) + } + } + } + + /// Retrieves the current blockchain height. + /// + /// This method communicates with the connected daemon to obtain the latest + /// blockchain height. It returns a `BlockHeight` on success or a `WalletError` on failure. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// + /// let manager = WalletManager::new().unwrap(); + /// let height = manager.get_height().unwrap(); + /// println!("Current blockchain height: {}", height); + /// ``` + pub fn get_height(&self) -> WalletResult { + unsafe { + let height = bindings::MONERO_WalletManager_blockchainHeight(self.ptr.as_ptr()); + // Assuming the FFI call does not set an error, directly return the height. + // If error handling is required, additional checks should be implemented here. + Ok(height) + } + } + + /// Sets the daemon address of WalletManager + pub fn set_daemon_address(&self, daemon_address: &str) -> WalletResult<()> { + let c_daemon_address = CString::new(daemon_address) + .map_err(|_| WalletError::FfiError("Invalid daemon address".to_string()))?; + + unsafe { + bindings::MONERO_WalletManager_setDaemonAddress( + self.ptr.as_ptr(), + c_daemon_address.as_ptr(), + ); + Ok(()) + } + } +} + +impl Wallet { + /// Retrieves the wallet's seed with an optional offset. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// + /// // Get seed with no offset + /// let seed = wallet.get_seed(None); + /// assert!(seed.is_ok(), "Failed to get seed: {:?}", seed.err()); + /// let seed = seed.unwrap(); + /// assert!(!seed.is_empty(), "Seed should not be empty"); + /// + /// // Get seed with an offset + /// let seed_with_offset = wallet.get_seed(Some("offset")); + /// assert!(seed_with_offset.is_ok(), "Failed to get seed with offset: {:?}", seed_with_offset.err()); + /// let seed_with_offset = seed_with_offset.unwrap(); + /// assert!(!seed_with_offset.is_empty(), "Seed with offset should not be empty"); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn get_seed(&self, seed_offset: Option<&str>) -> WalletResult { + let c_seed_offset = CString::new(seed_offset.unwrap_or("")) + .map_err(|_| WalletError::FfiError("Invalid seed_offset".to_string()))?; + + unsafe { + let seed_ptr = bindings::MONERO_Wallet_seed(self.ptr.as_ptr(), c_seed_offset.as_ptr()); + + self.throw_if_error()?; + if seed_ptr.is_null() { + return Err(self.get_last_error()); + } + + let seed = CStr::from_ptr(seed_ptr).to_string_lossy().into_owned(); + if seed.is_empty() { + return Err(WalletError::FfiError("Received empty seed".to_string())); + } + + Ok(seed) + } + } + + /// Retrieves the wallet's address for the given account and address index. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet).unwrap(); + /// let address = wallet.get_address(0, 0); + /// assert!(address.is_ok(), "Failed to get address: {:?}", address.err()); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn get_address(&self, account_index: u64, address_index: u64) -> WalletResult { + unsafe { + let address_ptr = + bindings::MONERO_Wallet_address(self.ptr.as_ptr(), account_index, address_index); + + self.throw_if_error()?; + if address_ptr.is_null() { + Err(self.get_last_error()) + } else { + let address = CStr::from_ptr(address_ptr).to_string_lossy().into_owned(); + Ok(address) + } + } + } + + /// Checks if the wallet is deterministic. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// let is_deterministic = wallet.is_deterministic(); + /// assert!(is_deterministic.is_ok(), "Failed to check if wallet is deterministic: {:?}", is_deterministic.err()); + /// assert!(is_deterministic.unwrap(), "Wallet should be deterministic"); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn is_deterministic(&self) -> WalletResult { + unsafe { + let result = bindings::MONERO_Wallet_isDeterministic(self.ptr.as_ptr()); + + self.throw_if_error()?; + Ok(result) + } + } + + /// Retrieves the last error from the wallet. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType, WalletError}; + /// let manager = WalletManager::new().unwrap(); + /// // Intentionally pass an invalid wallet to force an error. + /// let invalid_wallet = manager.create_wallet("", "", "", NetworkType::Mainnet); + /// if let Err(err) = invalid_wallet { + /// if let WalletError::WalletErrorCode(_, error_msg) = err { + /// // Check that an error message was produced + /// assert!(!error_msg.is_empty(), "Error message should not be empty"); + /// } + /// } + /// ``` + pub fn get_last_error(&self) -> WalletError { + unsafe { + let error_ptr = bindings::MONERO_Wallet_errorString(self.ptr.as_ptr()); + let status = bindings::MONERO_Wallet_status(self.ptr.as_ptr()); + + let error_msg = if error_ptr.is_null() { + "Unknown error".to_string() + } else { + CStr::from_ptr(error_ptr).to_string_lossy().into_owned() + }; + + WalletError::WalletErrorCode(status, error_msg) + } + } + + /// Checks for any errors by inspecting the wallet status and throws an error if found. + /// + /// # Returns + /// - `Ok(())` if no error is found. + /// - `Err(WalletError)` if an error is encountered. + pub fn throw_if_error(&self) -> WalletResult<()> { + let status_result = self.manager.get_status(self.ptr.as_ptr()); + if status_result.is_err() { + return status_result; // Return the error if the status is not OK. + } + Ok(()) + } + + /// Retrieves the balance and unlocked balance for the given account index. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType, WalletResult}; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let _wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet).unwrap(); + /// + /// let balance = _wallet.get_balance(0); + /// assert!(balance.is_ok(), "Failed to get balance: {:?}", balance.err()); + /// + /// // Clean up wallet files. + /// std::fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// std::fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn get_balance(&self, account_index: u32) -> WalletResult { + unsafe { + let balance = bindings::MONERO_Wallet_balance(self.ptr.as_ptr(), account_index); + + self.throw_if_error()?; + let unlocked_balance = + bindings::MONERO_Wallet_unlockedBalance(self.ptr.as_ptr(), account_index); + + self.throw_if_error()?; + Ok(GetBalance { + balance, + unlocked_balance, + }) + } + } + + /// Creates a new subaddress account with the given label. + /// + /// # Example + /// + /// ``` + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// // Set up the test environment. + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// // Initialize the wallet manager and create a wallet. + /// let manager = WalletManager::new().unwrap(); + /// let wallet_result = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + /// assert!(wallet_result.is_ok(), "Failed to create wallet: {:?}", wallet_result.err()); + /// let wallet = wallet_result.unwrap(); + /// + /// // Create a new account with a label. + /// let result = wallet.create_account("New Account"); + /// assert!(result.is_ok(), "Failed to create account: {:?}", result.err()); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn create_account(&self, label: &str) -> WalletResult<()> { + let c_label = + CString::new(label).map_err(|_| WalletError::FfiError("Invalid label".to_string()))?; + + unsafe { + bindings::MONERO_Wallet_addSubaddressAccount(self.ptr.as_ptr(), c_label.as_ptr()); + self.throw_if_error() + } + } + + /// Retrieves all accounts associated with the wallet. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet).expect("Failed to create wallet"); + /// + /// // Initially, there should be one account (the primary account). + /// let initial_accounts = wallet.get_accounts().expect("Failed to retrieve accounts"); + /// assert_eq!(initial_accounts.accounts.len(), 1, "Initial account count mismatch"); + /// assert_eq!(initial_accounts.accounts[0].label, "Primary account", "Expected primary account label"); + /// + /// // Create additional accounts. + /// wallet.create_account("Account 1").expect("Failed to create account 1"); + /// wallet.create_account("Account 2").expect("Failed to create account 2"); + /// + /// // Retrieve all accounts again; we should have three now. + /// let all_accounts = wallet.get_accounts().expect("Failed to retrieve all accounts"); + /// assert_eq!(all_accounts.accounts.len(), 3, "Expected 3 accounts after creation"); + /// + /// // Verify the labels of the accounts. + /// assert_eq!(all_accounts.accounts[0].label, "Primary account", "First account should be the primary account"); + /// assert_eq!(all_accounts.accounts[1].label, "Account 1", "Second account should be 'Account 1'"); + /// assert_eq!(all_accounts.accounts[2].label, "Account 2", "Third account should be 'Account 2'"); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn get_accounts(&self) -> WalletResult { + unsafe { + let accounts_size = bindings::MONERO_Wallet_numSubaddressAccounts(self.ptr.as_ptr()); + self.throw_if_error()?; + + let mut accounts = Vec::new(); + + for i in 0..accounts_size as u32 { + let label_ptr = bindings::MONERO_Wallet_getSubaddressLabel(self.ptr.as_ptr(), i, 0); + let label = if label_ptr.is_null() { + "Unnamed".to_string() + } else { + CStr::from_ptr(label_ptr).to_string_lossy().into_owned() + }; + + let balance = bindings::MONERO_Wallet_balance(self.ptr.as_ptr(), i); + let unlocked_balance = + bindings::MONERO_Wallet_unlockedBalance(self.ptr.as_ptr(), i); + + accounts.push(Account { + index: i, + label, + balance, + unlocked_balance, + }); + } + + Ok(GetAccounts { accounts }) + } + } + + /// Closes the wallet, releasing any resources associated with it. + /// + /// After calling this method, the `Wallet` instance should no longer be used. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType, WalletResult}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let mut wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet).unwrap(); + /// + /// // Use the wallet for operations... + /// + /// // Now close the wallet + /// let close_result = wallet.close_wallet(); + /// assert!(close_result.is_ok(), "Failed to close wallet: {:?}", close_result.err()); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn close_wallet(&mut self) -> WalletResult<()> { + if self.is_closed { + return Ok(()); + } + unsafe { + let result = bindings::MONERO_WalletManager_closeWallet( + self.manager.ptr.as_ptr(), + self.ptr.as_ptr(), + false, // Don't save the wallet by default. + ); + if result { + self.is_closed = true; + Ok(()) + } else { + Err(WalletError::FfiError("Failed to close wallet".to_string())) + } + } + } + + /// Initializes the wallet with the provided daemon settings. + /// + /// This method must be called after creating or opening a wallet to synchronize it + /// with the daemon and prepare it for operations like refreshing. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType, WalletConfig}; + /// use tempfile::TempDir; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + /// .expect("Failed to create wallet"); + /// + /// let config = WalletConfig { + /// daemon_address: "http://localhost:18081".to_string(), + /// upper_transaction_size_limit: 10000, + /// daemon_username: "user".to_string(), + /// daemon_password: "pass".to_string(), + /// use_ssl: false, + /// light_wallet: false, + /// proxy_address: "".to_string(), + /// }; + /// + /// let init_result = wallet.init(config); + /// assert!(init_result.is_ok(), "Failed to initialize wallet: {:?}", init_result.err()); + /// ``` + pub fn init(&self, config: WalletConfig) -> WalletResult<()> { + let c_daemon_address = CString::new(config.daemon_address) + .map_err(|_| WalletError::FfiError("Invalid daemon address".to_string()))?; + let c_daemon_username = CString::new(config.daemon_username) + .map_err(|_| WalletError::FfiError("Invalid daemon username".to_string()))?; + let c_daemon_password = CString::new(config.daemon_password) + .map_err(|_| WalletError::FfiError("Invalid daemon password".to_string()))?; + let c_proxy_address = CString::new(config.proxy_address) + .map_err(|_| WalletError::FfiError("Invalid proxy address".to_string()))?; + + unsafe { + let c_empty = CString::new("").unwrap(); + let c_log_tag = CString::new("moneroc").unwrap(); + bindings::MONERO_Wallet_init3( + self.ptr.as_ptr(), + c_empty.as_ptr(), + c_log_tag.as_ptr(), + c_empty.as_ptr(), + true, + ); + + let result = bindings::MONERO_Wallet_init( + self.ptr.as_ptr(), + c_daemon_address.into_raw(), + config.upper_transaction_size_limit, + c_daemon_username.into_raw(), + c_daemon_password.into_raw(), + config.use_ssl, + config.light_wallet, + c_proxy_address.into_raw(), + ); + + if result { + Ok(()) + } else { + // Retrieve the last error from the wallet + Err(self.get_last_error()) + } + } + } + + /// Refreshes the wallet's state by synchronizing it with the blockchain. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType, WalletConfig}; + /// use std::fs; + /// use tempfile::TempDir; + /// + /// fn main() { + /// // Create a temporary directory for testing purposes. + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + /// + /// // Initialize the WalletManager. + /// let manager = WalletManager::new().expect("Failed to create WalletManager"); + /// + /// // Create a new wallet. + /// let wallet = manager + /// .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + /// .expect("Failed to create wallet"); + /// + /// // Define the wallet initialization configuration. + /// let config = WalletConfig { + /// daemon_address: "http://localhost:18081".to_string(), + /// upper_transaction_size_limit: 10000, + /// daemon_username: "user".to_string(), + /// daemon_password: "pass".to_string(), + /// use_ssl: false, + /// light_wallet: false, + /// proxy_address: "".to_string(), + /// }; + /// + /// // Initialize the wallet with the specified configuration. + /// let init_result = wallet.init(config); + /// assert!(init_result.is_ok(), "Failed to initialize wallet: {:?}", init_result.err()); + /// + /// // Perform a refresh operation after initialization. + /// let refresh_result = wallet.refresh(); + /// assert!(refresh_result.is_ok(), "Failed to refresh wallet: {:?}", refresh_result.err()); + /// + /// // Optionally, you can verify the refresh by checking the blockchain height or other metrics. + /// // For example: + /// let height = manager.get_height().expect("Failed to get blockchain height"); + /// println!("Current blockchain height: {}", height); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// } + /// ``` + pub fn refresh(&self) -> WalletResult { + unsafe { + let result = bindings::MONERO_Wallet_refresh(self.ptr.as_ptr()); + + if result { + Ok(Refreshed) + } else { + // Retrieve the last error from the wallet + Err(self.get_last_error()) + } + } + } + + pub fn refresh_async(&self) -> WalletResult { + unsafe { + bindings::MONERO_Wallet_refreshAsync(self.ptr.as_ptr()); + return Ok(Refreshed); + } + } + + /// Initiates a transfer from the wallet to the specified destinations. + /// + /// # Returns + /// + /// * `WalletResult` - On success, returns a `Transfer` struct containing transaction details. + /// On failure, returns a `WalletError`. + pub fn transfer( + &self, + account_index: u32, + destinations: Vec, + get_tx_key: bool, + sweep_all: bool, + ) -> WalletResult { + // Define separators + let separator = ";"; + let separator_c = CString::new(separator) + .map_err(|_| WalletError::FfiError("Invalid separator".to_string()))?; + + // Concatenate destination addresses and amounts. + let addresses: Vec = destinations.iter().map(|d| d.address.clone()).collect(); + let address_list = addresses.join(separator); + let c_address_list = CString::new(address_list) + .map_err(|_| WalletError::FfiError("Invalid address list".to_string()))?; + + let amounts: Vec = destinations.iter().map(|d| d.amount.to_string()).collect(); + let amount_list = amounts.join(separator); + let c_amount_list = CString::new(amount_list) + .map_err(|_| WalletError::FfiError("Invalid amount list".to_string()))?; + + // TODO: Payment IDs. + let payment_id = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid payment_id".to_string()))?; + let mixin_count = 16; + + // Pending transaction priority - default to 0 (Default) + let pending_tx_priority = bindings::Priority_Default; + + // Subaddress account + let subaddr_account = account_index; + + // TODO: Preferred inputs. + let c_preferred_inputs = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid preferred inputs".to_string()))?; + + // Separator for preferred inputs + let preferred_inputs_separator = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid preferred inputs separator".to_string()))?; + + unsafe { + // Create the transaction with multiple destinations. + let tx_ptr = bindings::MONERO_Wallet_createTransactionMultDest( + self.ptr.as_ptr(), + c_address_list.as_ptr(), + separator_c.as_ptr(), + payment_id.as_ptr(), + sweep_all, + c_amount_list.as_ptr(), + separator_c.as_ptr(), + mixin_count, + pending_tx_priority, + subaddr_account, + c_preferred_inputs.as_ptr(), + preferred_inputs_separator.as_ptr(), + ); + + // Check for errors. + let ptr_as_mut_c_void = self.manager.ptr.as_ptr() as *mut c_void; + self.manager.throw_if_error(ptr_as_mut_c_void)?; + if tx_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + // Get the transaction ID. + let txid_ptr = bindings::MONERO_PendingTransaction_txid(tx_ptr, separator_c.as_ptr()); + if txid_ptr.is_null() { + return Err(WalletError::FfiError( + "Failed to get transaction ID".to_string(), + )); + } + let txid = CStr::from_ptr(txid_ptr).to_string_lossy().into_owned(); + + // Get the fee. + let fee = bindings::MONERO_PendingTransaction_fee(tx_ptr); + + // Optionally get the transaction key. + let tx_key = if get_tx_key { + let c_txid = CString::new(txid.clone()) + .map_err(|_| WalletError::FfiError("Invalid txid".to_string()))?; + let tx_key_ptr = + bindings::MONERO_Wallet_getTxKey(self.ptr.as_ptr(), c_txid.as_ptr()); + if tx_key_ptr.is_null() { + None + } else { + Some(CStr::from_ptr(tx_key_ptr).to_string_lossy().into_owned()) + } + } else { + None + }; + + // Submit the transaction. + // + // TODO: Make submission optional. + let tx_ptr_as_i8 = tx_ptr as *const i8; + let submit_result = + bindings::MONERO_Wallet_submitTransaction(self.ptr.as_ptr(), tx_ptr_as_i8); + if !submit_result { + return Err(WalletError::FfiError( + "Failed to submit transaction".to_string(), + )); + } + + Ok(Transfer { + txid, + tx_key, + amount: destinations.iter().map(|d| d.amount).sum(), + fee, + }) + } + } + + /// Sweep all funds from the specific account to the specified destination. + /// + /// TODO: Example / docs-tests. + pub fn sweep_all( + &self, + account_index: u32, + destination: Destination, + get_tx_key: bool, + ) -> WalletResult { + // Convert the destination address to a CString. + let c_address = CString::new(destination.address.clone()) + .map_err(|_| WalletError::FfiError("Invalid address".to_string()))?; + + // Placeholder values for fields not needed in sweep_all. + let empty_separator = + CString::new("").map_err(|_| WalletError::FfiError("Invalid separator".to_string()))?; + let payment_id = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid payment_id".to_string()))?; + let mixin_count = 16; + let pending_tx_priority = bindings::Priority_Default; + let c_preferred_inputs = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid preferred inputs".to_string()))?; + let preferred_inputs_separator = CString::new("") + .map_err(|_| WalletError::FfiError("Invalid preferred inputs separator".to_string()))?; + + unsafe { + // Create the sweep transaction. + let tx_ptr = bindings::MONERO_Wallet_createTransactionMultDest( + self.ptr.as_ptr(), + c_address.as_ptr(), + empty_separator.as_ptr(), + payment_id.as_ptr(), + true, // Sweep all funds. + empty_separator.as_ptr(), + empty_separator.as_ptr(), + mixin_count, + pending_tx_priority, + account_index, + c_preferred_inputs.as_ptr(), + preferred_inputs_separator.as_ptr(), + ); + + // Check for errors. + let ptr_as_mut_c_void = self.manager.ptr.as_ptr() as *mut c_void; + self.manager.throw_if_error(ptr_as_mut_c_void)?; + if tx_ptr.is_null() { + return Err(WalletError::NullPointer); + } + + // Get the transaction ID. + let txid_ptr = + bindings::MONERO_PendingTransaction_txid(tx_ptr, empty_separator.as_ptr()); + if txid_ptr.is_null() { + return Err(WalletError::FfiError( + "Failed to get transaction ID".to_string(), + )); + } + let txid = CStr::from_ptr(txid_ptr).to_string_lossy().into_owned(); + + // Get the fee. + let fee = bindings::MONERO_PendingTransaction_fee(tx_ptr); + + // Optionally get the transaction key. + let tx_key = if get_tx_key { + let c_txid = CString::new(txid.clone()) + .map_err(|_| WalletError::FfiError("Invalid txid".to_string()))?; + let tx_key_ptr = + bindings::MONERO_Wallet_getTxKey(self.ptr.as_ptr(), c_txid.as_ptr()); + if tx_key_ptr.is_null() { + None + } else { + Some(CStr::from_ptr(tx_key_ptr).to_string_lossy().into_owned()) + } + } else { + None + }; + + // Submit the transaction. + // + // TODO: Make submission optional. + let tx_ptr_as_i8 = tx_ptr as *const i8; + let submit_result = + bindings::MONERO_Wallet_submitTransaction(self.ptr.as_ptr(), tx_ptr_as_i8); + if !submit_result { + return Err(WalletError::FfiError( + "Failed to submit sweep transaction".to_string(), + )); + } + + Ok(Transfer { + txid, + tx_key, + amount: 0, // Since it's sweeping all, amount is not predefined. + fee, + }) + } + } + + /// Sets the seed language for the wallet. + /// + /// # Example + /// + /// ```rust + /// use monero_c_rust::{WalletManager, NetworkType}; + /// use tempfile::TempDir; + /// use std::fs; + /// + /// let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + /// let wallet_path = temp_dir.path().join("test_wallet"); + /// let wallet_str = wallet_path.to_str().unwrap(); + /// + /// let manager = WalletManager::new().unwrap(); + /// let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet).unwrap(); + /// + /// // Change the seed language to Spanish + /// let result = wallet.set_seed_language("Spanish"); + /// assert!(result.is_ok(), "Failed to set seed language: {:?}", result.err()); + /// + /// // Clean up wallet files. + /// fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + /// fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + /// ``` + pub fn set_seed_language(&self, language: &str) -> WalletResult<()> { + let c_language = CString::new(language) + .map_err(|_| WalletError::FfiError("Invalid language string".to_string()))?; + + unsafe { + bindings::MONERO_Wallet_setSeedLanguage(self.ptr.as_ptr(), c_language.as_ptr()); + self.throw_if_error() + } + } + + /// Checks the validity of a transaction key for a given transaction ID and address. + /// + /// This method verifies whether the provided transaction key (`tx_key`) is valid for the + /// specified transaction ID (`txid`) and address (`address`). If valid, it returns the + /// amount received, whether the transaction is in the pool, and the number of confirmations. + /// + /// TODO: Example / docs-tests. + pub fn check_tx_key( + &self, + txid: String, + tx_key: String, + address: String, + received: Option, + in_pool: Option, + confirmations: Option, + ) -> WalletResult { + // Convert Rust strings to C-compatible strings. + let c_txid = CString::new(txid) + .map_err(|_| WalletError::FfiError("Invalid txid string".to_string()))?; + let c_tx_key = CString::new(tx_key) + .map_err(|_| WalletError::FfiError("Invalid tx_key string".to_string()))?; + let c_address = CString::new(address) + .map_err(|_| WalletError::FfiError("Invalid address string".to_string()))?; + + // Assign default values if optional parameters are not provided. + let received_val = received.unwrap_or(0); + let in_pool_val = in_pool.unwrap_or(false); + let confirmations_val = confirmations.unwrap_or(0); + + // Call the C function. + let result = unsafe { + bindings::MONERO_Wallet_checkTxKey( + self.ptr.as_ptr(), + c_txid.as_ptr(), + c_tx_key.as_ptr(), + c_address.as_ptr(), + received_val, + in_pool_val, + confirmations_val, + ) + }; + + if result { + Ok(CheckTxKey { + valid: true, + error: None, + }) + } else { + // Retrieve the last error. + Err(WalletError::FfiError( + "Transaction key is invalid.".to_string(), + )) + } + } +} + +#[derive(Debug)] +pub struct GetBalance { + pub balance: u64, + pub unlocked_balance: u64, +} + +impl Drop for Wallet { + fn drop(&mut self) { + if !self.is_closed { + let _ = self.close_wallet(); + } + } +} + +#[cfg(test)] +use std::fs; +#[cfg(test)] +use tempfile::TempDir; + +#[cfg(test)] +fn check_and_delete_existing_wallets(temp_dir: &TempDir) -> std::io::Result<()> { + let test_wallet_names = &[ + "test_wallet", + "mainnet_wallet", + "testnet_wallet", + "stagenet_wallet", + ]; + + for name in test_wallet_names { + let wallet_file = temp_dir.path().join(name); + let keys_file = temp_dir.path().join(format!("{}.keys", name)); + + if wallet_file.exists() { + fs::remove_file(&wallet_file)?; + } + if keys_file.exists() { + fs::remove_file(&keys_file)?; + } + } + Ok(()) +} + +#[cfg(test)] +fn setup() -> WalletResult<(Arc, TempDir)> { + let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory"); + check_and_delete_existing_wallets(&temp_dir).expect("Failed to clean up existing wallets"); + + let manager = WalletManager::new()?; + Ok((manager, temp_dir)) +} + +#[cfg(test)] +fn teardown(temp_dir: &TempDir) -> std::io::Result<()> { + check_and_delete_existing_wallets(temp_dir) +} + +#[test] +fn test_wallet_manager_creation() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet_result = + manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + assert!(wallet_result.is_ok(), "WalletManager creation failed"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + assert!(wallet.is_ok(), "Failed to create wallet"); + + let wallet = wallet.unwrap(); + + assert!( + wallet.is_deterministic().is_ok(), + "Wallet creation seems to have failed" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_seed() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a new wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Test getting seed with no offset (None). + let result = wallet.get_seed(None); + assert!( + result.is_ok(), + "Failed to get seed without offset: {:?}", + result.err() + ); + assert!(!result.unwrap().is_empty(), "Seed without offset is empty"); + + // Test getting seed with a specific offset (Some("offset")). + let result_with_offset = wallet.get_seed(Some("offset")); + assert!( + result_with_offset.is_ok(), + "Failed to get seed with offset: {:?}", + result_with_offset.err() + ); + assert!( + !result_with_offset.unwrap().is_empty(), + "Seed with offset is empty" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_address() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + let result = wallet.get_address(0, 0); + assert!(result.is_ok(), "Failed to get address: {:?}", result.err()); + assert!(!result.unwrap().is_empty(), "Address is empty"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_is_deterministic() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + let result = wallet.is_deterministic(); + assert!( + result.is_ok(), + "Failed to check if wallet is deterministic: {:?}", + result.err() + ); + assert!(result.unwrap(), "Wallet should be deterministic"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation_with_different_networks() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallets = vec![ + ("mainnet_wallet", NetworkType::Mainnet), + ("testnet_wallet", NetworkType::Testnet), + ("stagenet_wallet", NetworkType::Stagenet), + ]; + + for (name, net_type) in wallets { + let wallet_path = temp_dir.path().join(name); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password", "English", net_type); + assert!(wallet.is_ok(), "Failed to create wallet: {}", name); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_restore_mnemonic_success() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + + // Example mnemonic seed (ensure this is a valid seed for your context). + let mnemonic_seed = "hemlock jubilee eden hacksaw boil superior inroads epoxy exhale orders cavernous second brunt saved richly lower upgrade hitched launching deepest mostly playful layout lower eden".to_string(); + + let wallet = manager.restore_mnemonic( + wallet_str.clone(), + "password".to_string(), + mnemonic_seed, + NetworkType::Mainnet, + 0, // Restore from the beginning of the blockchain. + 1, // Default KDF rounds. + "".to_string(), // No seed offset. + ); + assert!( + wallet.is_ok(), + "Failed to restore wallet: {:?}", + wallet.err() + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} +// TODO: Test with offset. + +#[test] +fn test_restore_polyseed_success() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + let polyseed = "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(); + + let restored_wallet = manager.restore_polyseed( + wallet_str.clone(), + "password".to_string(), + polyseed.clone(), + NetworkType::Mainnet, + 0, // Restore from the beginning of the blockchain. + 1, // Default KDF rounds. + "".to_string(), // No seed offset. + true, // Create a new wallet. + ); + assert!( + restored_wallet.is_ok(), + "Failed to restore wallet from polyseed: {:?}", + restored_wallet.err() + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_generate_from_keys_unit() { + println!("Running unit test: test_generate_from_keys_unit"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("generated_wallet_unit"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Test parameters. + // + // TODO add functions to get spend and view keys. + let address = "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi"; + let spendkey = "29adefc8f67515b4b4bf48031780ab9d071d24f8a674b879ce7f245c37523807"; + let viewkey = "3bc0b202cde92fe5719c3cc0a16aa94f88a5d19f8c515d4e35fae361f6f2120e"; + let restore_height = 0; + let password = "password"; + let language = "English"; + let network_type = NetworkType::Mainnet; + let kdf_rounds = 1; + + let result = manager.generate_from_keys( + wallet_str.to_string(), + address.to_string(), + spendkey.to_string(), + viewkey.to_string(), + restore_height, + password.to_string(), + language.to_string(), + network_type, + kdf_rounds, + ); + assert!( + result.is_ok(), + "Failed to generate wallet from keys: {:?}", + result.err() + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_multiple_address_generation() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + for i in 0..5 { + let result = wallet.get_address(0, i); + assert!( + result.is_ok(), + "Failed to get address {}: {:?}", + i, + result.err() + ); + assert!(!result.unwrap().is_empty(), "Address {} is empty", i); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_error_display() { + // Test WalletError::FfiError variant. + let error = WalletError::FfiError("Test error".to_string()); + match error { + WalletError::FfiError(msg) => assert_eq!(msg, "Test error"), + _ => panic!("Expected FfiError variant"), + } + + // Test WalletError::NullPointer variant. + let error = WalletError::NullPointer; + match error { + WalletError::NullPointer => assert!(true), + _ => panic!("Expected NullPointer variant"), + } + + // Test WalletError::WalletErrorCode variant. + let error = WalletError::WalletErrorCode(2, "Sample wallet error".to_string()); + match error { + WalletError::WalletErrorCode(code, msg) => { + assert_eq!(code, 2); + assert_eq!(msg, "Sample wallet error"); + } + _ => panic!("Expected WalletErrorCode variant"), + } +} + +#[test] +fn test_wallet_status() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet to use for status checking + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Check the status of the wallet, expecting it to be OK + let status_result = manager.get_status(wallet.ptr.as_ptr()); + assert!( + status_result.is_ok(), + "Failed to get status: {:?}", + status_result.err() + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_open_wallet() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet to be opened later + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Drop the wallet so it can be opened later + drop(wallet); + + // Try to open the wallet + let open_result = manager.open_wallet(wallet_str, "password", NetworkType::Mainnet); + assert!( + open_result.is_ok(), + "Failed to open wallet: {:?}", + open_result.err() + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_balance() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .unwrap(); + + let balance_result = wallet.get_balance(0); + assert!( + balance_result.is_ok(), + "Failed to get balance: {:?}", + balance_result.err() + ); + + let _balance = balance_result.unwrap(); + // assert!(_balance.balance >= 0, "Balance should be non-negative"); + // assert!(_balance.unlocked_balance >= 0, "Unlocked balance should be non-negative"); + // These assertions are meaningless with the constraints of the type. + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_create_account() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Create a new account. + let result = wallet.create_account("Test Account"); + assert!( + result.is_ok(), + "Failed to create account: {:?}", + result.err() + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_accounts() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Add two accounts for testing + wallet + .create_account("Test Account 1") + .expect("Failed to create account 1"); + wallet + .create_account("Test Account 2") + .expect("Failed to create account 2"); + + // Retrieve all accounts + let accounts = wallet.get_accounts().expect("Failed to retrieve accounts"); + assert_eq!(accounts.accounts.len(), 3); // Including the primary account + + // Check account names + assert_eq!(accounts.accounts[0].label, "Primary account"); + assert_eq!(accounts.accounts[1].label, "Test Account 1"); + assert_eq!(accounts.accounts[2].label, "Test Account 2"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_close_wallet() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet. + let mut wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Close the wallet. + let close_result = wallet.close_wallet(); + assert!( + close_result.is_ok(), + "Failed to close wallet: {:?}", + close_result.err() + ); + + // Attempt to close the wallet again. + let close_again_result = wallet.close_wallet(); + assert!( + close_again_result.is_ok(), + "Failed to close wallet a second time: {:?}", + close_again_result.err() + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_height_success() { + let manager = WalletManager::new().unwrap(); + let height = manager.get_height().unwrap(); + // assert!(height > 0, "Blockchain height should be greater than 0"); + // The test should not assume network connectivity/any syncing progress, so: + assert!(height == 0, "Blockchain height should be equal to 0"); + } +} + +#[test] +fn test_init_success() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Define initialization configuration. + let config = WalletConfig { + daemon_address: "http://localhost:18081".to_string(), + upper_transaction_size_limit: 10000, + daemon_username: "user".to_string(), + daemon_password: "pass".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + }; + + // Initialize the wallet. + let init_result = wallet.init(config); + assert!( + init_result.is_ok(), + "Failed to initialize wallet: {:?}", + init_result.err() + ); + + // Clean up wallet files. + fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_refresh_success() { + println!("Running test_refresh_success"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Wallet created successfully."); + + // Define initialization configuration. + let config = WalletConfig { + daemon_address: "http://localhost:18081".to_string(), + upper_transaction_size_limit: 10000, + daemon_username: "user".to_string(), + daemon_password: "pass".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + }; + + // Perform the initialization. + println!("Initializing the wallet..."); + let init_result = wallet.init(config); + + assert!( + init_result.is_ok(), + "Failed to initialize wallet: {:?}", + init_result.err() + ); + + // Perform a refresh operation after initialization. + println!("Refreshing the wallet..."); + let refresh_result = wallet.refresh(); + assert!( + refresh_result.is_ok(), + "Failed to refresh wallet: {:?}", + refresh_result.err() + ); + + // Clean up wallet files. + fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_set_seed_language() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet_set_seed_language"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a new wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Set the seed language to Spanish. + let result = wallet.set_seed_language("Spanish"); + assert!( + result.is_ok(), + "Failed to set seed language: {:?}", + result.err() + ); + + // Optionally, retrieve the seed language to verify it was set correctly. + // This requires implementing a corresponding `get_seed_language` method. + // For now, we'll assume that if no error was returned, the operation was successful. + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_check_tx_key() { + let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + + let mnemonic_seed = "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(); + let passphrase = "".to_string(); + + // Restore the wallet using polyseed. + let wallet = WalletManager::new() + .expect("Failed to create WalletManager") + .restore_polyseed( + wallet_str.clone(), + "password".to_string(), + mnemonic_seed.clone(), + NetworkType::Mainnet, + 0, + 1, + passphrase.clone(), + true, + ) + .expect("Failed to restore wallet from polyseed"); + + // Print the primary address. + println!( + "Primary address: {}", + wallet.get_address(0, 0).expect("Failed to get address") + ); + + // Valid transaction details. + let valid_txid = "b3f1b71f5127f9d655e58f7a2b324a64bfbc5a3ea1ce8846a0f4c51cbcb87ea6".to_string(); + let valid_tx_key = + "48ef9d8b772c4f5097e29a4ba413605497d978c74e879fda67545dddff312b0a".to_string(); + let valid_address = "465cUW8wTMSCV8oVVh7CuWWHs7yeB1oxhNPrsEM5FKSqadTXmobLqsNEtRnyGsbN1rbDuBtWdtxtXhTJda1Lm9vcH2ZdrD1".to_string(); + + // Check the transaction key. + let valid_check = wallet.check_tx_key( + valid_txid.clone(), + valid_tx_key.clone(), + valid_address.clone(), + Some(1), + Some(false), + Some(10), + ); + + match valid_check { + Ok(check) => { + assert!(check.valid, "Valid transaction key should be valid."); + assert!( + check.error.is_none(), + "There should be no error for valid transaction key." + ); + println!("Valid transaction key check passed."); + } + Err(e) => { + panic!("Error checking valid transaction key: {:?}", e); + } + } + + // Clean up wallet files. + fs::remove_file(&wallet_path).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_path.display())) + .expect("Failed to delete test wallet keys"); +} + +#[test] +fn test_invalid_check_tx_key() { + let temp_dir = TempDir::new().expect("Failed to create temporary directory"); + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + + let mnemonic_seed = "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(); + let passphrase = "".to_string(); + + // Restore the wallet using polyseed. + let wallet = WalletManager::new() + .expect("Failed to create WalletManager") + .restore_polyseed( + wallet_str.clone(), + "password".to_string(), + mnemonic_seed.clone(), + NetworkType::Mainnet, + 0, + 1, + passphrase.clone(), + true, + ) + .expect("Failed to restore wallet from polyseed"); + + // Print the primary address. + println!( + "Primary address: {}", + wallet.get_address(0, 0).expect("Failed to get address") + ); + + // Invalid transaction details. + let invalid_txid = "invalid_tx_id".to_string(); + let invalid_tx_key = "invalid_tx_key".to_string(); + let invalid_address = "invalid_address".to_string(); + + // Check the invalid transaction key. + let invalid_check = wallet.check_tx_key( + invalid_txid.clone(), + invalid_tx_key.clone(), + invalid_address.clone(), + Some(1), + Some(false), + Some(10), + ); + + match invalid_check { + Ok(check) => { + assert!(!check.valid, "Invalid transaction key should be invalid."); + assert!( + check.error.is_some(), + "There should be an error message for invalid transaction key." + ); + println!("Invalid transaction key check correctly identified as invalid."); + } + Err(e) => { + println!("Expected error for invalid transaction key: {:?}", e); + } + } + + // Clean up wallet files. + fs::remove_file(&wallet_path).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_path.display())) + .expect("Failed to delete test wallet keys"); +} diff --git a/impls/monero.rs/tests/integration_tests.rs b/impls/monero.rs/tests/integration_tests.rs new file mode 100644 index 00000000..0b425a6c --- /dev/null +++ b/impls/monero.rs/tests/integration_tests.rs @@ -0,0 +1,1014 @@ +use monero_c_rust::{ + NetworkType, WalletConfig, WalletError, WalletManager, WalletResult, WalletStatus_Ok, +}; +use std::fs; +use std::sync::Arc; +use std::time::Instant; +use tempfile::TempDir; + +const TEST_WALLET_NAMES: &[&str] = &[ + "test_wallet", + "mainnet_wallet", + "testnet_wallet", + "stagenet_wallet", +]; + +/// Helper function to clean up existing wallet files in a temporary directory. +fn check_and_delete_existing_wallets(temp_dir: &TempDir) -> std::io::Result<()> { + for name in TEST_WALLET_NAMES { + // Construct absolute paths for wallet files. + let wallet_file = temp_dir.path().join(name); + let keys_file = temp_dir.path().join(format!("{}.keys", name)); + let address_file = temp_dir.path().join(format!("{}.address.txt", name)); // Added + + // Delete wallet file if it exists. + if wallet_file.exists() { + if let Err(e) = fs::remove_file(&wallet_file) { + println!( + "Warning: Failed to delete wallet file {:?}: {}", + wallet_file, e + ); + } else { + println!("Deleted existing wallet file: {:?}", wallet_file); + } + } + + // Delete keys file if it exists. + if keys_file.exists() { + if let Err(e) = fs::remove_file(&keys_file) { + println!("Warning: Failed to delete keys file {:?}: {}", keys_file, e); + } else { + println!("Deleted existing keys file: {:?}", keys_file); + } + } + + // Delete address file if it exists. + if address_file.exists() { + if let Err(e) = fs::remove_file(&address_file) { + println!( + "Warning: Failed to delete address file {:?}: {}", + address_file, e + ); + } else { + println!("Deleted existing address file: {:?}", address_file); + } + } + } + Ok(()) +} + +/// Sets up the test environment by creating a temporary directory and initializing the WalletManager. +/// +/// Returns: +/// - An `Arc` wrapped `WalletManager` instance. +/// - A `TempDir` representing the temporary directory. +fn setup() -> WalletResult<(Arc, TempDir)> { + println!("Setting up test environment..."); + let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory"); + check_and_delete_existing_wallets(&temp_dir).expect("Failed to clean up existing wallets"); + + println!("Creating WalletManager..."); + let start = Instant::now(); + let manager = WalletManager::new()?; + println!("WalletManager creation took {:?}", start.elapsed()); + + Ok((manager, temp_dir)) +} + +/// Tears down the test environment by deleting wallet files. +/// +/// Args: +/// - `temp_dir`: Reference to the temporary directory. +/// +/// Returns: +/// - `Result<(), std::io::Error>` indicating success or failure. +fn teardown(temp_dir: &TempDir) -> std::io::Result<()> { + println!("Tearing down test environment..."); + check_and_delete_existing_wallets(temp_dir) +} + +#[test] +fn test_wallet_manager_creation() { + println!("Running test_wallet_manager_creation"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet_result = + manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + assert!( + wallet_result.is_ok(), + "WalletManager creation seems to have failed" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password", "English", NetworkType::Mainnet); + assert!(wallet.is_ok(), "Failed to create wallet"); + let wallet = wallet.unwrap(); + assert!( + wallet.is_deterministic().is_ok(), + "Wallet creation seems to have failed" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_restore_mnemonic_integration() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + let mnemonic_seed = "hemlock jubilee eden hacksaw boil superior inroads epoxy exhale orders cavernous second brunt saved richly lower upgrade hitched launching deepest mostly playful layout lower eden".to_string(); + + let restored_wallet = manager.restore_mnemonic( + wallet_str.clone(), + "password".to_string(), + mnemonic_seed, + NetworkType::Mainnet, + 0, // Restore from the beginning of the blockchain. + 1, // Default KDF rounds. + "".to_string(), // No seed offset. + ); + + assert!( + restored_wallet.is_ok(), + "Failed to restore wallet: {:?}", + restored_wallet.err() + ); + + // Check that the wallet is deterministic. + let wallet = restored_wallet.unwrap(); + assert!( + wallet.is_deterministic().is_ok(), + "Restored wallet seems to have failed" + ); + assert!( + wallet.is_deterministic().unwrap(), + "Restored wallet should be deterministic" + ); + + // Optionally, verify the address if applicable. + let address_result = wallet.get_address(0, 0); + assert!( + address_result.is_ok(), + "Failed to retrieve address: {:?}", + address_result.err() + ); + let address = address_result.unwrap(); + assert_eq!( + address, + "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi", + "Address does not match expected value" + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} +// TODO: Test with offset. + +#[test] +fn test_restore_polyseed_integration() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string") + .to_string(); + let mnemonic_seed = "capital chief route liar question fix clutch water outside pave hamster occur always learn license knife".to_string(); + + let restored_wallet = manager.restore_polyseed( + wallet_str.clone(), + "password".to_string(), + mnemonic_seed, + NetworkType::Mainnet, + 0, // Restore from the beginning of the blockchain. + 1, // Default KDF rounds. + "".to_string(), // No seed offset. + true, // Create a new wallet. + ); + + assert!( + restored_wallet.is_ok(), + "Failed to restore wallet: {:?}", + restored_wallet.err() + ); + + // Check that the wallet is deterministic. + let wallet = restored_wallet.unwrap(); + assert!( + wallet.is_deterministic().is_ok(), + "Restored wallet seems to have failed" + ); + assert!( + wallet.is_deterministic().unwrap(), + "Restored wallet should be deterministic" + ); + + // Optionally, verify the address if applicable. + let address_result = wallet.get_address(0, 0); + assert!( + address_result.is_ok(), + "Failed to retrieve address: {:?}", + address_result.err() + ); + let address = address_result.unwrap(); + assert_eq!( + address, + "465cUW8wTMSCV8oVVh7CuWWHs7yeB1oxhNPrsEM5FKSqadTXmobLqsNEtRnyGsbN1rbDuBtWdtxtXhTJda1Lm9vcH2ZdrD1", + "Address does not match expected value" + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_generate_from_keys_integration() { + println!("Running test_generate_from_keys_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.generate_from_keys( + wallet_str.to_string(), + "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi".to_string(), + "29adefc8f67515b4b4bf48031780ab9d071d24f8a674b879ce7f245c37523807".to_string(), + "3bc0b202cde92fe5719c3cc0a16aa94f88a5d19f8c515d4e35fae361f6f2120e".to_string(), + 0, + "password".to_string(), + "English".to_string(), + NetworkType::Mainnet, + 1, // KDF rounds. + ); + + assert!( + wallet.is_ok(), + "Failed to generate wallet from keys: {:?}", + wallet.err() + ); + + // Verify that the wallet was generated correctly. + let wallet = wallet.expect("Failed to create wallet"); + + // This is required even though "English" was passed as the seed language above. + let set_language_result = wallet.set_seed_language("English"); + assert!( + set_language_result.is_ok(), + "Failed to set seed language: {:?}", + set_language_result.err() + ); + + // The address should be "45wsWad9...". + let address_result = wallet.get_address(0, 0); + assert!( + address_result.is_ok(), + "Failed to get address: {:?}", + address_result.err() + ); + let address = address_result.unwrap(); + assert_eq!(address, "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi"); + + // Get the seed. It should be "hemlock jubilee...". + let seed_result = wallet.get_seed(None); + assert!( + seed_result.is_ok(), + "Failed to get seed: {:?}", + seed_result.err() + ); + let seed = seed_result.unwrap(); + assert_eq!(seed, "hemlock jubilee eden hacksaw boil superior inroads epoxy exhale orders cavernous second brunt saved richly lower upgrade hitched launching deepest mostly playful layout lower eden"); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_generate_view_only_from_keys_integration() { + println!("Running test_generate_from_keys_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.generate_from_keys( + wallet_str.to_string(), + "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi".to_string(), + "".to_string(), + "3bc0b202cde92fe5719c3cc0a16aa94f88a5d19f8c515d4e35fae361f6f2120e".to_string(), + 0, + "password".to_string(), + "English".to_string(), + NetworkType::Mainnet, + 1, // KDF rounds. + ); + + assert!( + wallet.is_ok(), + "Failed to generate wallet from keys: {:?}", + wallet.err() + ); + + // Verify that the wallet was generated correctly. + let wallet = wallet.expect("Failed to create wallet"); + + // This is required even though "English" was passed as the seed language above. + let set_language_result = wallet.set_seed_language("English"); + assert!( + set_language_result.is_ok(), + "Failed to set seed language: {:?}", + set_language_result.err() + ); + + // The address should be "45wsWad9...". + let address_result = wallet.get_address(0, 0); + assert!( + address_result.is_ok(), + "Failed to get address: {:?}", + address_result.err() + ); + let address = address_result.unwrap(); + assert_eq!(address, "45wsWad9EwZgF3VpxQumrUCRaEtdyyh6NG8sVD3YRVVJbK1jkpJ3zq8WHLijVzodQ22LxwkdWx7fS2a6JzaRGzkNU8K2Dhi"); + + // The wallet should not be deterministic. + let is_deterministic_result = wallet.is_deterministic(); + assert!( + is_deterministic_result.is_ok(), + "Failed to check if wallet is deterministic: {:?}", + is_deterministic_result.err() + ); + assert!( + !is_deterministic_result.unwrap(), + "Wallet should not be deterministic" + ); + + // Clean up wallet files. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_seed() { + println!("Running test_get_seed"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Test getting seed without offset. + println!("Attempting to get seed without offset..."); + let start = Instant::now(); + let result = wallet.get_seed(None); + println!("get_seed without offset took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to get seed: {:?}", result.err()); + assert!(!result.unwrap().is_empty(), "Seed is empty"); + + // Test getting seed with an offset. + println!("Attempting to get seed with offset..."); + let start = Instant::now(); + let result_with_offset = wallet.get_seed(Some("example_offset")); + println!("get_seed with offset took {:?}", start.elapsed()); + assert!( + result_with_offset.is_ok(), + "Failed to get seed with offset: {:?}", + result_with_offset.err() + ); + assert!( + !result_with_offset.unwrap().is_empty(), + "Seed with offset is empty" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_address() { + println!("Running test_get_address"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Attempting to get address..."); + let start = Instant::now(); + let result = wallet.get_address(0, 0); + println!("get_address took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to get address: {:?}", result.err()); + assert!(!result.unwrap().is_empty(), "Address is empty"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_is_deterministic() { + println!("Running test_is_deterministic"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Checking if wallet is deterministic..."); + let start = Instant::now(); + let result = wallet.is_deterministic(); + println!("is_deterministic check took {:?}", start.elapsed()); + assert!( + result.is_ok(), + "Failed to check if wallet is deterministic: {:?}", + result.err() + ); + assert!(result.unwrap(), "Wallet should be deterministic"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation_with_different_networks() { + println!("Running test_wallet_creation_with_different_networks"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Define wallet names and corresponding network types. + let wallets = vec![ + ("mainnet_wallet", NetworkType::Mainnet), + ("testnet_wallet", NetworkType::Testnet), + ("stagenet_wallet", NetworkType::Stagenet), + ]; + + for (name, net_type) in wallets { + println!("Creating wallet: {} on network type {:?}", name, net_type); + + // Construct the full path for each wallet within temp_dir. + let wallet_path = temp_dir.path().join(name); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password", "English", net_type); + assert!(wallet.is_ok(), "Failed to create wallet: {}", name); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_multiple_address_generation() { + println!("Running test_multiple_address_generation"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + for i in 0..5 { + println!("Generating address {}...", i); + let start = Instant::now(); + let result = wallet.get_address(0, i); + println!("Address generation took {:?}", start.elapsed()); + assert!( + result.is_ok(), + "Failed to get address {}: {:?}", + i, + result.err() + ); + assert!(!result.unwrap().is_empty(), "Address {} is empty", i); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_error_display() { + println!("Running test_wallet_error_display"); + + // Test WalletError::FfiError variant. + let error = WalletError::FfiError("Test error".to_string()); + match error { + WalletError::FfiError(msg) => assert_eq!(msg, "Test error"), + _ => panic!("Expected FfiError variant"), + } + + // Test WalletError::NullPointer variant. + let error = WalletError::NullPointer; + match error { + WalletError::NullPointer => assert!(true), + _ => panic!("Expected NullPointer variant"), + } + + // Test WalletError::WalletErrorCode variant. + let error = WalletError::WalletErrorCode(2, "Sample wallet error".to_string()); + match error { + WalletError::WalletErrorCode(code, msg) => { + assert_eq!(code, 2); + assert_eq!(msg, "Sample wallet error"); + } + _ => panic!("Expected WalletErrorCode variant"), + } +} + +#[test] +fn test_wallet_status_integration() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Create a wallet. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Check the status of the wallet. + let status = manager.get_status(wallet.ptr.as_ptr()); + assert!( + status.is_ok(), + "Expected status OK, got error: {:?}", + status.err() + ); + + // Clean up. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_open_wallet_integration() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Create a wallet. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Drop the wallet to simulate closing it. + drop(wallet); + + // Try opening the wallet. + let open_result = manager.open_wallet(wallet_str, "password", NetworkType::Mainnet); + assert!( + open_result.is_ok(), + "Failed to open wallet: {:?}", + open_result.err() + ); + + // Clean up. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_open_wallet_invalid_password() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create a wallet with a valid password. + let wallet = manager + .create_wallet( + wallet_str, + "correct_password", + "English", + NetworkType::Mainnet, + ) + .expect("Failed to create wallet"); + + // Drop the wallet + drop(wallet); + + // Attempt to open the wallet with an incorrect password. + let open_result = manager.open_wallet(wallet_str, "wrong_password", NetworkType::Mainnet); + assert!( + open_result.is_err(), + "Expected an error when opening wallet with incorrect password" + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_open_wallet_invalid_path() { + let (manager, _temp_dir) = setup().expect("Failed to set up test environment"); + + // Try to open a wallet at a non-existent path. + let invalid_path = "/invalid/path/to/non_existent_wallet"; + let open_result = manager.open_wallet(invalid_path, "password", NetworkType::Mainnet); + + // Check if the result is an error. + match open_result { + Err(e) => { + // Inspect the error to check the specific status and error message. + if let WalletError::WalletErrorCode(status, error_message) = e { + assert_ne!( + status, WalletStatus_Ok, + "Expected a non-OK status code, got OK instead." + ); + assert!( + error_message.contains("file not found") + || error_message.contains("openWallet"), + "Unexpected error message: {}", + error_message + ); + } else { + panic!("Expected WalletErrorCode, got {:?}", e); + } + } + Ok(_) => panic!("Expected an error when opening a non-existent wallet, but it succeeded."), + } +} + +#[test] +fn test_get_balance_integration() { + println!("Running test_get_balance_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Fetch the balance. + println!("Fetching wallet balance..."); + let start = Instant::now(); + let balance_result = wallet.get_balance(0); // Account index 0. + println!("Fetching balance took {:?}", start.elapsed()); + + assert!( + balance_result.is_ok(), + "Failed to fetch balance: {:?}", + balance_result.err() + ); + + let balance = balance_result.unwrap(); + println!("Balance: {:?}", balance); + + // Ensure the balance values make sense. + // assert!(balance.balance >= 0, "Balance should be non-negative"); + // assert!(balance.unlocked_balance >= 0, "Unlocked balance should be non-negative"); + // These assertions are meaningless with the constraints of the type. + // TODO: Test with scanning integration. + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_create_account_integration() { + println!("Running test_create_account_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Create a new account with a label. + println!("Creating a new account..."); + let start = Instant::now(); + let result = wallet.create_account("Test Account Integration"); + println!("create_account took {:?}", start.elapsed()); + + assert!( + result.is_ok(), + "Failed to create account: {:?}", + result.err() + ); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_accounts_integration() { + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Add multiple accounts. + wallet + .create_account("Integration Account 1") + .expect("Failed to create account"); + wallet + .create_account("Integration Account 2") + .expect("Failed to create account"); + + // Fetch accounts. + let accounts_result = wallet.get_accounts(); + assert!( + accounts_result.is_ok(), + "Failed to fetch accounts: {:?}", + accounts_result.err() + ); + let accounts = accounts_result.unwrap().accounts; + assert_eq!(accounts.len(), 3, "Expected 3 accounts"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_close_wallet_integration() { + println!("Running test_close_wallet_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let mut wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Use the wallet for operations... + println!("Using the wallet for operations..."); + + // Close the wallet. + println!("Closing the wallet..."); + let start = Instant::now(); + let close_result = wallet.close_wallet(); + println!("close_wallet took {:?}", start.elapsed()); + assert!( + close_result.is_ok(), + "Failed to close wallet: {:?}", + close_result.err() + ); + + // Attempt to close the wallet again. + println!("Attempting to close the wallet again..."); + let start = Instant::now(); + let close_again_result = wallet.close_wallet(); + println!("Second close_wallet call took {:?}", start.elapsed()); + assert!( + close_again_result.is_ok(), + "Failed to close wallet a second time: {:?}", + close_again_result.err() + ); + + // Clean up. + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_height_integration() { + println!("Running test_get_height_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet_height"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let _wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + + // Fetch the blockchain height. + println!("Fetching blockchain height..."); + let start = Instant::now(); + let height_result = manager.get_height(); + let duration = start.elapsed(); + println!("Blockchain height retrieval took {:?}", duration); + + assert!( + height_result.is_ok(), + "Failed to fetch blockchain height: {:?}", + height_result.err() + ); + + let height = height_result.unwrap(); + println!("Current blockchain height: {}", height); + assert!(height == 0, "Blockchain height should be equal to 0."); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_refresh_integration_success() { + println!("Running test_refresh_integration_success"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("refresh_integration_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Wallet created successfully."); + + // Define initialization configuration. + let config = WalletConfig { + daemon_address: "http://localhost:18081".to_string(), + upper_transaction_size_limit: 10000, // TODO: use sane value. + daemon_username: "user".to_string(), + daemon_password: "pass".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + }; + + // Perform the initialization. + println!("Initializing the wallet..."); + let start = Instant::now(); + let init_result = wallet.init(config); + let duration = start.elapsed(); + println!("Initialization took {:?}", duration); + + assert!( + init_result.is_ok(), + "Failed to initialize wallet: {:?}", + init_result.err() + ); + + // Perform a refresh operation after initialization. + println!("Refreshing the wallet..."); + let refresh_start = Instant::now(); + let refresh_result = wallet.refresh(); + let refresh_duration = refresh_start.elapsed(); + println!("Refresh operation took {:?}", refresh_duration); + + assert!( + refresh_result.is_ok(), + "Failed to refresh wallet: {:?}", + refresh_result.err() + ); + + // Clean up wallet files. + fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_init_integration_success() { + println!("Running test_init_integration_success"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Wallet created successfully."); + + // Define initialization configuration. + let config = WalletConfig { + daemon_address: "http://localhost:18081".to_string(), + upper_transaction_size_limit: 10000, + daemon_username: "user".to_string(), + daemon_password: "pass".to_string(), + use_ssl: false, + light_wallet: false, + proxy_address: "".to_string(), + }; + + // Perform the initialization. + println!("Initializing the wallet..."); + let start = Instant::now(); + let init_result = wallet.init(config); + let duration = start.elapsed(); + println!("Initialization took {:?}", duration); + + assert!( + init_result.is_ok(), + "Failed to initialize wallet: {:?}", + init_result.err() + ); + + // Perform a refresh operation after initialization. + println!("Refreshing the wallet..."); + let refresh_result = wallet.refresh(); + assert!( + refresh_result.is_ok(), + "Failed to refresh wallet after initialization: {:?}", + refresh_result.err() + ); + + // Clean up wallet files. + fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_set_seed_language_integration() { + println!("Running test_set_seed_language_integration"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("set_seed_language_wallet"); + let wallet_str = wallet_path + .to_str() + .expect("Failed to convert wallet path to string"); + + // Create the wallet. + let wallet = manager + .create_wallet(wallet_str, "password", "English", NetworkType::Mainnet) + .expect("Failed to create wallet"); + println!("Wallet created successfully."); + + // Set the seed language to French. + println!("Setting seed language to French..."); + let start = Instant::now(); + let set_language_result = wallet.set_seed_language("French"); + let duration = start.elapsed(); + println!("set_seed_language took {:?}", duration); + + assert!( + set_language_result.is_ok(), + "Failed to set seed language: {:?}", + set_language_result.err() + ); + + // Optionally, verify the seed language by retrieving it. + // This requires implementing a corresponding `get_seed_language` method. + + // Clean up wallet files. + fs::remove_file(wallet_str).expect("Failed to delete test wallet"); + fs::remove_file(format!("{}.keys", wallet_str)).expect("Failed to delete test wallet keys"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts index 03bb061d..542a6b96 100644 --- a/impls/monero.ts/checksum_monero.ts +++ b/impls/monero.ts/checksum_monero.ts @@ -1,5 +1,5 @@ export const moneroChecksum = { - wallet2_api_c_h_sha256: "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116", - wallet2_api_c_cpp_sha256: "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", + wallet2_api_c_h_sha256: "6c1ba9b57cb185c6dad030b15bcffe8a4772f33930e7f1d62d23b33514ba6f62", + wallet2_api_c_cpp_sha256: "1d1deff340408541f5755b4838d06345f63dcdfffe26b14dbdce32a5de839c55-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", wallet2_api_c_exp_sha256: "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d", } diff --git a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h index 02869fe4..d2cd521a 100644 --- a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h +++ b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h @@ -1,6 +1,6 @@ #ifndef MONEROC_CHECKSUMS #define MONEROC_CHECKSUMS -const char * MONERO_wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; -const char * MONERO_wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const char * MONERO_wallet2_api_c_h_sha256 = "6c1ba9b57cb185c6dad030b15bcffe8a4772f33930e7f1d62d23b33514ba6f62"; +const char * MONERO_wallet2_api_c_cpp_sha256 = "1d1deff340408541f5755b4838d06345f63dcdfffe26b14dbdce32a5de839c55-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; const char * MONERO_wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; #endif diff --git a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp index 19192cf6..0617a97e 100644 --- a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp +++ b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp @@ -1972,6 +1972,19 @@ const char* MONERO_Wallet_getTxKey(void* wallet_ptr, const char* txid) { DEBUG_END() } +bool MONERO_Wallet_checkTxKey(void* wallet_ptr, const char* txid, const char* tx_key, const char* address, uint64_t received, bool in_pool, uint64_t confirmations) { + Monero::Wallet* wallet = reinterpret_cast(wallet_ptr); + bool result = wallet->checkTxKey( + std::string(txid), + std::string(tx_key), + std::string(address), + received, + in_pool, + confirmations + ); + return result; +} + const char* MONERO_Wallet_signMessage(void* wallet_ptr, const char* message, const char* address) { DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); diff --git a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h index 17c647c5..a0094b7e 100644 --- a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h +++ b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h @@ -778,6 +778,7 @@ extern ADDAPI const char* MONERO_Wallet_getUserNote(void* wallet_ptr, const char // virtual std::string getTxKey(const std::string &txid) const = 0; extern ADDAPI const char* MONERO_Wallet_getTxKey(void* wallet_ptr, const char* txid); // virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; +extern ADDAPI bool MONERO_Wallet_checkTxKey(void* wallet_ptr, const char* txid, const char* tx_key, const char* address, uint64_t received, bool in_pool, uint64_t confirmations); // virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0; // virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; // virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;