diff options
| author | sneurlax <sneurlax@gmail.com> | 2024-10-10 21:26:30 -0500 |
|---|---|---|
| committer | sneurlax <sneurlax@gmail.com> | 2024-10-10 21:26:30 -0500 |
| commit | c2c2f2c00dba3b7253627ddfaec37b2d015ba36b (patch) | |
| tree | 9ec455da551e730e87ac573b929cf6f9244bb82d /impls | |
| parent | 4694bb913f7709c2b2fa568f065cc56f92f09555 (diff) | |
allow lib to be loaded by consumer crates
and document the required placement
Diffstat (limited to 'impls')
| -rw-r--r-- | impls/monero_rust/Cargo.lock | 1 | ||||
| -rw-r--r-- | impls/monero_rust/Cargo.toml | 1 | ||||
| -rw-r--r-- | impls/monero_rust/README.md | 8 | ||||
| -rw-r--r-- | impls/monero_rust/example/Cargo.lock | 295 | ||||
| -rw-r--r-- | impls/monero_rust/example/Cargo.toml | 7 | ||||
| -rw-r--r-- | impls/monero_rust/example/src/main.rs | 25 | ||||
| -rw-r--r-- | impls/monero_rust/src/lib.rs | 147 | ||||
| -rw-r--r-- | impls/monero_rust/src/main.rs | 6 |
8 files changed, 437 insertions, 53 deletions
diff --git a/impls/monero_rust/Cargo.lock b/impls/monero_rust/Cargo.lock index 2d20545..bf7f0e5 100644 --- a/impls/monero_rust/Cargo.lock +++ b/impls/monero_rust/Cargo.lock @@ -124,6 +124,7 @@ version = "0.0.1" dependencies = [ "bindgen", "libc", + "libloading", ] [[package]] diff --git a/impls/monero_rust/Cargo.toml b/impls/monero_rust/Cargo.toml index 548823e..af37515 100644 --- a/impls/monero_rust/Cargo.toml +++ b/impls/monero_rust/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["lib", "cdylib"] [dependencies] libc = "0.2" +libloading = "0.8.5" [build-dependencies] bindgen = "0.70.1" diff --git a/impls/monero_rust/README.md b/impls/monero_rust/README.md index 5affbd1..87d23a3 100644 --- a/impls/monero_rust/README.md +++ b/impls/monero_rust/README.md @@ -10,7 +10,7 @@ sudo apt-get install libhidapi-dev ``` --> ### Build `monero_c` -Build the monero_c Library for your architecture. Follow the upstream docs at +Build the monero_c library for your architecture. Follow the upstream docs at https://github.com/MrCyjaneK/monero_c <!-- TODO: use example CMakeLists --> and place the library at `monero_c/impls/monero_rust/lib/libwallet2_api_c.so` or use the provided script: @@ -35,8 +35,12 @@ mv release/monero/x86_64-linux-gnu_libwallet2_api_c.so ../lib/libwallet2_api_c.s # The library should be at monero_c/impls/monero_rust/lib/libwallet2_api_c.so. ``` -### Run `monero_rust` example +### Run `monero_rust` demo From `monero_c/impls/monero_rust`: ``` cargo run ``` + +## Using `monero_rust` in your own crate +Refer to the `example` folder. `libwallet2_api_c.so` must be in the same +directory as the binary (*eg.* at `example/target/debug/libwallet2_api_c.so`). diff --git a/impls/monero_rust/example/Cargo.lock b/impls/monero_rust/example/Cargo.lock new file mode 100644 index 0000000..d921e0d --- /dev/null +++ b/impls/monero_rust/example/Cargo.lock @@ -0,0 +1,295 @@ +# 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 = "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 = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[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 = "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 = "monero_example" +version = "0.0.1" +dependencies = [ + "monero_rust", +] + +[[package]] +name = "monero_rust" +version = "0.0.1" +dependencies = [ + "bindgen", + "libc", + "libloading", +] + +[[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 = "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 = "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 = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[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_rust/example/Cargo.toml b/impls/monero_rust/example/Cargo.toml new file mode 100644 index 0000000..e0b7823 --- /dev/null +++ b/impls/monero_rust/example/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "monero_example" +version = "0.0.1" +edition = "2021" + +[dependencies] +monero_rust = { path = ".." } diff --git a/impls/monero_rust/example/src/main.rs b/impls/monero_rust/example/src/main.rs new file mode 100644 index 0000000..aae688b --- /dev/null +++ b/impls/monero_rust/example/src/main.rs @@ -0,0 +1,25 @@ +use monero_rust::{WalletError, WalletManager, NETWORK_TYPE_MAINNET}; + +fn main() -> Result<(), WalletError> { + let wallet_manager = WalletManager::new(None)?; + + let wallet = wallet_manager.create_wallet( + "wallet", + "password", + "English", + NETWORK_TYPE_MAINNET, + )?; + + match wallet.get_seed("") { + Ok(seed) => println!("Seed: {}", seed), + Err(e) => { + eprintln!("Failed to get seed: {:?}", e); + return Err(e); + } + } + + let address = wallet.get_address(0, 0)?; + println!("Wallet Address: {}", address); + + Ok(()) +} diff --git a/impls/monero_rust/src/lib.rs b/impls/monero_rust/src/lib.rs index d576ea6..2175083 100644 --- a/impls/monero_rust/src/lib.rs +++ b/impls/monero_rust/src/lib.rs @@ -1,62 +1,69 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int, c_void}; use std::ptr::NonNull; +use std::sync::Arc; + +use libloading::{Library, Symbol}; pub const NETWORK_TYPE_MAINNET: c_int = 0; pub const NETWORK_TYPE_TESTNET: c_int = 1; pub const NETWORK_TYPE_STAGENET: c_int = 2; -extern "C" { - pub fn MONERO_WalletManagerFactory_getWalletManager() -> *mut c_void; - pub fn MONERO_WalletManager_createWallet( - wm_ptr: *mut c_void, - path: *const c_char, - password: *const c_char, - language: *const c_char, - networkType: c_int, - ) -> *mut c_void; - pub fn MONERO_Wallet_seed( - wallet_ptr: *mut c_void, - seed_offset: *const c_char, - ) -> *const c_char; - pub fn MONERO_WalletManager_closeWallet( - wm_ptr: *mut c_void, - wallet_ptr: *mut c_void, - store: bool, - ) -> bool; - pub fn MONERO_Wallet_errorString(wallet_ptr: *mut c_void) -> *const c_char; - pub fn MONERO_Wallet_status(wallet_ptr: *mut c_void) -> c_int; - pub fn MONERO_Wallet_address( - wallet_ptr: *mut c_void, - account_index: u64, - address_index: u64, - ) -> *const c_char; - pub fn MONERO_Wallet_isDeterministic(wallet_ptr: *mut c_void) -> bool; - pub fn MONERO_WalletManager_walletExists( - wm_ptr: *mut c_void, - path: *const c_char, - ) -> bool; -} - #[derive(Debug)] pub enum WalletError { NullPointer, FfiError(String), WalletErrorCode(c_int, String), + LibraryLoadError(String), } -type WalletResult<T> = Result<T, WalletError>; +pub type WalletResult<T> = Result<T, WalletError>; pub struct WalletManager { ptr: NonNull<c_void>, + library: Arc<Library>, } impl WalletManager { - pub fn new() -> WalletResult<Self> { + /// Create a new WalletManager, optionally specifying the path to the shared library. + pub fn new(lib_path: Option<&str>) -> WalletResult<Self> { + let lib = match lib_path { + Some(path) => { + unsafe { + Library::new(path).map_err(|e| WalletError::LibraryLoadError(e.to_string()))? + } + } + None => { + // Attempt to load from the same directory as the executable. + let exe_path = std::env::current_exe() + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?; + let exe_dir = exe_path.parent().ok_or_else(|| { + WalletError::LibraryLoadError("Failed to get executable directory".to_string()) + })?; + let lib_path = exe_dir.join("libwallet2_api_c.so"); + + unsafe { + if lib_path.exists() { + Library::new(lib_path) + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))? + } else { + // Fallback to standard locations. + Library::new("libwallet2_api_c.so") + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))? + } + } + } + }; + + let library = Arc::new(lib); + unsafe { - let ptr = MONERO_WalletManagerFactory_getWalletManager(); + let func: Symbol<unsafe extern "C" fn() -> *mut c_void> = + library.get(b"MONERO_WalletManagerFactory_getWalletManager\0") + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?; + let ptr = func(); NonNull::new(ptr) - .map(|nn_ptr| WalletManager { ptr: nn_ptr }) + .map(|nn_ptr| WalletManager { ptr: nn_ptr, library }) .ok_or(WalletError::NullPointer) } } @@ -73,7 +80,19 @@ impl WalletManager { let c_language = CString::new(language).expect("CString::new failed"); unsafe { - let wallet_ptr = MONERO_WalletManager_createWallet( + let func: Symbol< + unsafe extern "C" fn( + *mut c_void, + *const c_char, + *const c_char, + *const c_char, + c_int, + ) -> *mut c_void, + > = self + .library + .get(b"MONERO_WalletManager_createWallet\0") + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?; + let wallet_ptr = func( self.ptr.as_ptr(), c_path.as_ptr(), c_password.as_ptr(), @@ -87,15 +106,24 @@ impl WalletManager { Ok(Wallet { ptr: NonNull::new_unchecked(wallet_ptr), manager_ptr: self.ptr, + library: Arc::clone(&self.library), // Keep the library alive. }) } } } } +impl Drop for WalletManager { + fn drop(&mut self) { + // The library will be dropped automatically when all references are gone. + } +} + +#[derive(Clone)] pub struct Wallet { ptr: NonNull<c_void>, manager_ptr: NonNull<c_void>, + library: Arc<Library>, } impl Wallet { @@ -103,7 +131,11 @@ impl Wallet { let c_seed_offset = CString::new(seed_offset).expect("CString::new failed"); unsafe { - let seed_ptr = MONERO_Wallet_seed(self.ptr.as_ptr(), c_seed_offset.as_ptr()); + let func: Symbol<unsafe extern "C" fn(*mut c_void, *const c_char) -> *const c_char> = + self.library + .get(b"MONERO_Wallet_seed\0") + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?; + let seed_ptr = func(self.ptr.as_ptr(), c_seed_offset.as_ptr()); if seed_ptr.is_null() { Err(self.get_last_error()) @@ -122,11 +154,11 @@ impl Wallet { pub fn get_address(&self, account_index: u64, address_index: u64) -> WalletResult<String> { unsafe { - let address_ptr = MONERO_Wallet_address( - self.ptr.as_ptr(), - account_index, - address_index, - ); + let func: Symbol<unsafe extern "C" fn(*mut c_void, u64, u64) -> *const c_char> = + self.library + .get(b"MONERO_Wallet_address\0") + .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?; + let address_ptr = func(self.ptr.as_ptr(), account_index, address_index); if address_ptr.is_null() { Err(self.get_last_error()) @@ -140,13 +172,28 @@ impl Wallet { } pub fn is_deterministic(&self) -> bool { - unsafe { MONERO_Wallet_isDeterministic(self.ptr.as_ptr()) } + unsafe { + let func: Symbol<unsafe extern "C" fn(*mut c_void) -> bool> = self + .library + .get(b"MONERO_Wallet_isDeterministic\0") + .expect("Failed to load MONERO_Wallet_isDeterministic"); + func(self.ptr.as_ptr()) + } } fn get_last_error(&self) -> WalletError { unsafe { - let error_ptr = MONERO_Wallet_errorString(self.ptr.as_ptr()); - let status = MONERO_Wallet_status(self.ptr.as_ptr()); + let error_func: Symbol<unsafe extern "C" fn(*mut c_void) -> *const c_char> = self + .library + .get(b"MONERO_Wallet_errorString\0") + .expect("Failed to load MONERO_Wallet_errorString"); + let status_func: Symbol<unsafe extern "C" fn(*mut c_void) -> c_int> = self + .library + .get(b"MONERO_Wallet_status\0") + .expect("Failed to load MONERO_Wallet_status"); + + let error_ptr = error_func(self.ptr.as_ptr()); + let status = status_func(self.ptr.as_ptr()); let error_msg = if error_ptr.is_null() { "Unknown error".to_string() @@ -164,7 +211,13 @@ impl Wallet { impl Drop for Wallet { fn drop(&mut self) { unsafe { - MONERO_WalletManager_closeWallet(self.manager_ptr.as_ptr(), self.ptr.as_ptr(), false); + let func: Symbol< + unsafe extern "C" fn(*mut c_void, *mut c_void, bool) -> bool, + > = self + .library + .get(b"MONERO_WalletManager_closeWallet\0") + .expect("Failed to load MONERO_WalletManager_closeWallet"); + func(self.manager_ptr.as_ptr(), self.ptr.as_ptr(), false); } } } diff --git a/impls/monero_rust/src/main.rs b/impls/monero_rust/src/main.rs index eb9e148..aae688b 100644 --- a/impls/monero_rust/src/main.rs +++ b/impls/monero_rust/src/main.rs @@ -1,9 +1,7 @@ -use monero_rust::{ - WalletError, WalletManager, NETWORK_TYPE_MAINNET, -}; +use monero_rust::{WalletError, WalletManager, NETWORK_TYPE_MAINNET}; fn main() -> Result<(), WalletError> { - let wallet_manager = WalletManager::new()?; + let wallet_manager = WalletManager::new(None)?; let wallet = wallet_manager.create_wallet( "wallet", |
