summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsneurlax <sneurlax@gmail.com>2024-10-14 21:35:00 -0500
committersneurlax <sneurlax@gmail.com>2024-10-14 21:35:00 -0500
commit774091b550cac6df4f25127adb4047cf4334b973 (patch)
treed39b91a2f5c719c93f47c6125aa8bd5a482686d5
parenta046d9002345918bc39dd53c8b13885b2db84263 (diff)
do not use libloading
-rw-r--r--impls/monero.rs/Cargo.lock4
-rw-r--r--impls/monero.rs/Cargo.toml6
-rw-r--r--impls/monero.rs/build.rs82
-rw-r--r--impls/monero.rs/src/lib.rs172
-rw-r--r--impls/monero.rs/tests/integration_tests.rs11
5 files changed, 111 insertions, 164 deletions
diff --git a/impls/monero.rs/Cargo.lock b/impls/monero.rs/Cargo.lock
index 9e79d0f..9991a45 100644
--- a/impls/monero.rs/Cargo.lock
+++ b/impls/monero.rs/Cargo.lock
@@ -185,12 +185,10 @@ dependencies = [
]
[[package]]
-name = "monero_rust"
+name = "monero_c_rust"
version = "0.0.1"
dependencies = [
"bindgen",
- "libc",
- "libloading",
"mockall",
"tempfile",
]
diff --git a/impls/monero.rs/Cargo.toml b/impls/monero.rs/Cargo.toml
index fa0e6ab..f055225 100644
--- a/impls/monero.rs/Cargo.toml
+++ b/impls/monero.rs/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "monero_rust"
+name = "monero_c_rust"
version = "0.0.1"
edition = "2021"
description = "monero_c Rust bindings."
@@ -8,13 +8,11 @@ license = "MIT"
build = "build.rs"
[lib]
-name = "monero_rust"
+name = "monero_c_rust"
path = "src/lib.rs"
crate-type = ["lib", "cdylib"]
[dependencies]
-libc = "0.2"
-libloading = "0.8.5"
mockall = "0.13.0"
tempfile = "3.13.0"
diff --git a/impls/monero.rs/build.rs b/impls/monero.rs/build.rs
index 357028b..19a4394 100644
--- a/impls/monero.rs/build.rs
+++ b/impls/monero.rs/build.rs
@@ -1,13 +1,86 @@
use std::env;
+use std::fs::{self, OpenOptions};
+use std::io::Write;
use std::path::PathBuf;
use bindgen::EnumVariation;
+#[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");
- // Configure bindgen
+ 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=stdc++");
+ println!("cargo:rustc-link-lib=dylib=hidapi-hidraw");
+ 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_.*")
@@ -28,18 +101,17 @@ fn main() {
.blocklist_type("_.*")
.blocklist_function("__.*")
.layout_tests(false)
- .default_enum_style(EnumVariation::Rust {
- non_exhaustive: 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)
+ .write_to_file(out_path.clone())
.expect("Couldn't write bindings!");
}
diff --git a/impls/monero.rs/src/lib.rs b/impls/monero.rs/src/lib.rs
index c9b1d81..7cc334c 100644
--- a/impls/monero.rs/src/lib.rs
+++ b/impls/monero.rs/src/lib.rs
@@ -1,21 +1,9 @@
use std::ffi::{CStr, CString};
-use std::os::raw::{c_char, c_int, c_void};
-use std::path::PathBuf;
+use std::os::raw::{c_int, c_void};
use std::ptr::NonNull;
use std::sync::Arc;
-use libloading::{Library, Symbol};
-
-#[cfg(target_os = "android")]
-const LIB_NAME: &str = "libmonero_libwallet2_api_c.so";
-#[cfg(target_os = "ios")]
-const LIB_NAME: &str = "MoneroWallet.framework/MoneroWallet";
-#[cfg(target_os = "linux")]
-const LIB_NAME: &str = "monero_libwallet2_api_c.so";
-#[cfg(target_os = "macos")]
-const LIB_NAME: &str = "monero_libwallet2_api_c.dylib";
-#[cfg(target_os = "windows")]
-const LIB_NAME: &str = "monero_libwallet2_api_c.dll";
+mod bindings;
pub mod network {
use std::os::raw::c_int;
@@ -29,32 +17,25 @@ pub enum WalletError {
NullPointer,
FfiError(String),
WalletErrorCode(c_int, String),
- LibraryLoadError(String),
}
pub type WalletResult<T> = Result<T, WalletError>;
pub struct WalletManager {
ptr: NonNull<c_void>,
- library: Library,
}
impl WalletManager {
- /// Creates a new `WalletManager`, loading the Monero wallet library (`wallet2_api_c`).
- pub fn new(lib_path: Option<&str>) -> WalletResult<Arc<Self>> {
- let library = Self::load_library(lib_path)?;
-
+ /// Creates a new `WalletManager` using the statically linked `MONERO_WalletManagerFactory_getWalletManager`.
+ pub fn new() -> WalletResult<Arc<Self>> {
unsafe {
- 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 = NonNull::new(func()).ok_or(WalletError::NullPointer)?;
-
- Ok(Arc::new(WalletManager { ptr, library }))
+ let ptr = bindings::MONERO_WalletManagerFactory_getWalletManager();
+ let ptr = NonNull::new(ptr).ok_or(WalletError::NullPointer)?;
+ Ok(Arc::new(WalletManager { ptr }))
}
}
+ /// Creates a new wallet.
pub fn create_wallet(
self: &Arc<Self>,
path: &str,
@@ -62,28 +43,12 @@ impl WalletManager {
language: &str,
network_type: c_int,
) -> WalletResult<Wallet> {
- 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()))?;
+ 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 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(
+ let wallet_ptr = bindings::MONERO_WalletManager_createWallet(
self.ptr.as_ptr(),
c_path.as_ptr(),
c_password.as_ptr(),
@@ -92,62 +57,10 @@ impl WalletManager {
);
NonNull::new(wallet_ptr)
- .map(|ptr| Wallet {
- ptr,
- manager: Arc::clone(self),
- })
+ .map(|ptr| Wallet { ptr, manager: Arc::clone(self) })
.ok_or(WalletError::NullPointer)
}
}
-
- fn load_library(lib_path: Option<&str>) -> WalletResult<Library> {
- if let Some(path) = lib_path {
- unsafe { Library::new(path).map_err(|e| WalletError::LibraryLoadError(e.to_string())) }
- } else {
- 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 candidates = Self::get_library_candidates(exe_dir);
-
- candidates
- .into_iter()
- .find_map(|path| unsafe { Library::new(&path).ok() })
- .ok_or_else(|| {
- WalletError::LibraryLoadError(format!(
- "Failed to load {} from standard paths",
- LIB_NAME
- ))
- })
- }
- }
-
- fn get_library_candidates(exe_dir: &std::path::Path) -> Vec<PathBuf> {
- let mut candidates = Vec::new();
-
- // Candidate 1: ../../../../release/ relative to the executable.
- if let Some(lib_dir) = exe_dir.ancestors().nth(4) {
- candidates.push(lib_dir.join("release").join(LIB_NAME));
- }
-
- // Candidate 2: ../../lib/ relative to the executable.
- if let Some(lib_dir) = exe_dir.ancestors().nth(2) {
- candidates.push(lib_dir.join("lib").join(LIB_NAME));
- }
-
- // Candidate 3: Same directory as the executable.
- candidates.push(exe_dir.join(LIB_NAME));
- // TODO: This should probably be the first candidate for binary
- // distribution purposes; it will likely be the first place the library
- // will be found in a binary distribution.
-
- // Candidate 4: Standard library paths.
- candidates.push(PathBuf::from(LIB_NAME));
-
- candidates
- }
}
pub struct Wallet {
@@ -156,19 +69,13 @@ pub struct Wallet {
}
impl Wallet {
+ /// Retrieves the wallet's seed with an optional offset.
pub fn get_seed(&self, seed_offset: &str) -> WalletResult<String> {
let c_seed_offset = CString::new(seed_offset)
.map_err(|_| WalletError::FfiError("Invalid seed_offset".to_string()))?;
unsafe {
- let func: Symbol<unsafe extern "C" fn(*mut c_void, *const c_char) -> *const c_char> =
- self.manager
- .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());
-
+ let seed_ptr = bindings::MONERO_Wallet_seed(self.ptr.as_ptr(), c_seed_offset.as_ptr());
if seed_ptr.is_null() {
Err(self.get_last_error())
} else {
@@ -184,15 +91,10 @@ impl Wallet {
}
}
+ /// Retrieves the wallet's address for the given account and address index.
pub fn get_address(&self, account_index: u64, address_index: u64) -> WalletResult<String> {
unsafe {
- let func: Symbol<unsafe extern "C" fn(*mut c_void, u64, u64) -> *const c_char> =
- self.manager
- .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);
-
+ let address_ptr = bindings::MONERO_Wallet_address(self.ptr.as_ptr(), account_index, address_index);
if address_ptr.is_null() {
Err(self.get_last_error())
} else {
@@ -204,32 +106,19 @@ impl Wallet {
}
}
+ /// Checks if the wallet is deterministic.
pub fn is_deterministic(&self) -> WalletResult<bool> {
unsafe {
- let func: Symbol<unsafe extern "C" fn(*mut c_void) -> bool> = self
- .manager
- .library
- .get(b"MONERO_Wallet_isDeterministic\0")
- .map_err(|e| WalletError::LibraryLoadError(e.to_string()))?;
- Ok(func(self.ptr.as_ptr()))
+ let result = bindings::MONERO_Wallet_isDeterministic(self.ptr.as_ptr());
+ Ok(result)
}
}
- fn get_last_error(&self) -> WalletError {
+ /// Retrieves the last error from the wallet.
+ pub fn get_last_error(&self) -> WalletError {
unsafe {
- let error_func: Symbol<unsafe extern "C" fn(*mut c_void) -> *const c_char> = self
- .manager
- .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
- .manager
- .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_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()
@@ -247,14 +136,11 @@ impl Wallet {
impl Drop for Wallet {
fn drop(&mut self) {
unsafe {
- let func: Symbol<
- unsafe extern "C" fn(*mut c_void, *mut c_void, bool) -> bool,
- > = self
- .manager
- .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);
+ bindings::MONERO_WalletManager_closeWallet(
+ self.manager.ptr.as_ptr(),
+ self.ptr.as_ptr(),
+ false, // Don't save the wallet by default.
+ );
}
}
}
diff --git a/impls/monero.rs/tests/integration_tests.rs b/impls/monero.rs/tests/integration_tests.rs
index 168e882..beb28e2 100644
--- a/impls/monero.rs/tests/integration_tests.rs
+++ b/impls/monero.rs/tests/integration_tests.rs
@@ -1,4 +1,4 @@
-use monero_rust::{WalletManager, network, WalletError, WalletResult};
+use monero_c_rust::{WalletManager, network, WalletError, WalletResult};
use std::fs;
use std::sync::Arc;
use std::time::Instant;
@@ -61,7 +61,7 @@ fn setup() -> WalletResult<(Arc<WalletManager>, TempDir)> {
println!("Creating WalletManager...");
let start = Instant::now();
- let manager = WalletManager::new(None)?;
+ let manager = WalletManager::new()?;
println!("WalletManager creation took {:?}", start.elapsed());
Ok((manager, temp_dir))
@@ -255,11 +255,4 @@ fn test_wallet_error_display() {
},
_ => panic!("Expected WalletErrorCode variant"),
}
-
- // Test WalletError::LibraryLoadError variant.
- let error = WalletError::LibraryLoadError("Failed to load library".to_string());
- match error {
- WalletError::LibraryLoadError(msg) => assert_eq!(msg, "Failed to load library"),
- _ => panic!("Expected LibraryLoadError variant"),
- }
}