mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-28 20:09:55 +00:00
336 lines
9.9 KiB
Rust
336 lines
9.9 KiB
Rust
//! Wallet lifecycle management functions.
|
|
|
|
use std::{
|
|
ffi::{c_char, CStr, CString},
|
|
path::PathBuf,
|
|
ptr,
|
|
sync::Mutex,
|
|
};
|
|
|
|
use wallet::WalletCore;
|
|
|
|
use crate::{
|
|
c_str_to_string,
|
|
error::{print_error, WalletFfiError},
|
|
types::WalletHandle,
|
|
};
|
|
|
|
/// Internal wrapper around `WalletCore` with mutex for thread safety.
|
|
pub(crate) struct WalletWrapper {
|
|
pub core: Mutex<WalletCore>,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct FfiCreateWalletResult {
|
|
pub wallet: *mut WalletHandle,
|
|
/// C compatible(null terminated) string.
|
|
pub mnemonic: *mut *const c_char,
|
|
}
|
|
|
|
impl Default for FfiCreateWalletResult {
|
|
fn default() -> Self {
|
|
Self {
|
|
wallet: std::ptr::null_mut(),
|
|
mnemonic: std::ptr::null_mut(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Helper to get the wallet wrapper from an opaque handle.
|
|
pub(crate) fn get_wallet(
|
|
handle: *mut WalletHandle,
|
|
) -> Result<&'static WalletWrapper, WalletFfiError> {
|
|
if handle.is_null() {
|
|
print_error("Null wallet handle");
|
|
return Err(WalletFfiError::NullPointer);
|
|
}
|
|
Ok(unsafe { &*handle.cast::<WalletWrapper>() })
|
|
}
|
|
|
|
/// Helper to get a mutable reference to the wallet wrapper.
|
|
#[expect(dead_code, reason = "Maybe used later")]
|
|
pub(crate) fn get_wallet_mut(
|
|
handle: *mut WalletHandle,
|
|
) -> Result<&'static mut WalletWrapper, WalletFfiError> {
|
|
if handle.is_null() {
|
|
print_error("Null wallet handle");
|
|
return Err(WalletFfiError::NullPointer);
|
|
}
|
|
Ok(unsafe { &mut *handle.cast::<WalletWrapper>() })
|
|
}
|
|
|
|
/// Helper to convert a C string to a Rust `PathBuf`.
|
|
fn c_str_to_path(ptr: *const c_char, name: &str) -> Result<PathBuf, WalletFfiError> {
|
|
if ptr.is_null() {
|
|
print_error(format!("Null pointer for {name}"));
|
|
return Err(WalletFfiError::NullPointer);
|
|
}
|
|
|
|
let c_str = unsafe { CStr::from_ptr(ptr) };
|
|
match c_str.to_str() {
|
|
Ok(s) => Ok(PathBuf::from(s)),
|
|
Err(e) => {
|
|
print_error(format!("Invalid UTF-8 in {name}: {e}"));
|
|
Err(WalletFfiError::InvalidUtf8)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Create a new wallet with fresh storage.
|
|
///
|
|
/// This initializes a new wallet with a new seed derived from the password.
|
|
/// Use this for first-time wallet creation.
|
|
///
|
|
/// # Parameters
|
|
/// - `config_path`: Path to the wallet configuration file (JSON)
|
|
/// - `storage_path`: Path where wallet data will be stored
|
|
/// - `password`: Password for encrypting the wallet seed
|
|
///
|
|
/// # Returns
|
|
/// - Result, which contains opaque wallet handle and mnemonic words on success
|
|
/// - Result with null pointers on error (call `wallet_ffi_get_last_error()` for details)
|
|
///
|
|
/// # Safety
|
|
/// All string parameters must be valid null-terminated UTF-8 strings.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_create_new(
|
|
config_path: *const c_char,
|
|
storage_path: *const c_char,
|
|
password: *const c_char,
|
|
) -> FfiCreateWalletResult {
|
|
let Ok(config_path) = c_str_to_path(config_path, "config_path") else {
|
|
return FfiCreateWalletResult::default();
|
|
};
|
|
|
|
let Ok(storage_path) = c_str_to_path(storage_path, "storage_path") else {
|
|
return FfiCreateWalletResult::default();
|
|
};
|
|
|
|
let Ok(password) = c_str_to_string(password, "password") else {
|
|
return FfiCreateWalletResult::default();
|
|
};
|
|
|
|
match WalletCore::new_init_storage(config_path, storage_path, None, &password) {
|
|
Ok((core, mnemonic)) => {
|
|
let wrapper = Box::new(WalletWrapper {
|
|
core: Mutex::new(core),
|
|
});
|
|
let handle = Box::into_raw(wrapper).cast::<WalletHandle>();
|
|
|
|
let Ok(c_mnemonic_string) = CString::new(mnemonic.to_string()) else {
|
|
return FfiCreateWalletResult::default();
|
|
};
|
|
|
|
let boxed_mnemonic_string = Box::new(c_mnemonic_string.as_ptr());
|
|
let raw_mnemonic_string_pointer = Box::into_raw(boxed_mnemonic_string);
|
|
|
|
FfiCreateWalletResult {
|
|
wallet: handle,
|
|
mnemonic: raw_mnemonic_string_pointer,
|
|
}
|
|
}
|
|
Err(e) => {
|
|
print_error(format!("Failed to create wallet: {e}"));
|
|
FfiCreateWalletResult::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Open an existing wallet from storage.
|
|
///
|
|
/// This loads a wallet that was previously created with `wallet_ffi_create_new()`.
|
|
///
|
|
/// # Parameters
|
|
/// - `config_path`: Path to the wallet configuration file (JSON)
|
|
/// - `storage_path`: Path where wallet data is stored
|
|
///
|
|
/// # Returns
|
|
/// - Opaque wallet handle on success
|
|
/// - Null pointer on error (call `wallet_ffi_get_last_error()` for details)
|
|
///
|
|
/// # Safety
|
|
/// All string parameters must be valid null-terminated UTF-8 strings.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_open(
|
|
config_path: *const c_char,
|
|
storage_path: *const c_char,
|
|
) -> *mut WalletHandle {
|
|
let Ok(config_path) = c_str_to_path(config_path, "config_path") else {
|
|
return ptr::null_mut();
|
|
};
|
|
|
|
let Ok(storage_path) = c_str_to_path(storage_path, "storage_path") else {
|
|
return ptr::null_mut();
|
|
};
|
|
|
|
match WalletCore::new_update_chain(config_path, storage_path, None) {
|
|
Ok(core) => {
|
|
let wrapper = Box::new(WalletWrapper {
|
|
core: Mutex::new(core),
|
|
});
|
|
Box::into_raw(wrapper).cast::<WalletHandle>()
|
|
}
|
|
Err(e) => {
|
|
print_error(format!("Failed to open wallet: {e}"));
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Destroy a wallet handle and free its resources.
|
|
///
|
|
/// After calling this function, the handle is invalid and must not be used.
|
|
///
|
|
/// # Safety
|
|
/// - The handle must be either null or a valid handle from `wallet_ffi_create_new()` or
|
|
/// `wallet_ffi_open()`.
|
|
/// - The handle must not be used after this call.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_destroy(handle: *mut WalletHandle) {
|
|
if !handle.is_null() {
|
|
unsafe {
|
|
drop(Box::from_raw(handle.cast::<WalletWrapper>()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Save wallet state to persistent storage.
|
|
///
|
|
/// This should be called periodically or after important operations to ensure
|
|
/// wallet data is persisted to disk.
|
|
///
|
|
/// # Parameters
|
|
/// - `handle`: Valid wallet handle
|
|
///
|
|
/// # Returns
|
|
/// - `Success` on successful save
|
|
/// - Error code on failure
|
|
///
|
|
/// # Safety
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_save(handle: *mut WalletHandle) -> WalletFfiError {
|
|
let wrapper = match get_wallet(handle) {
|
|
Ok(w) => w,
|
|
Err(e) => return e,
|
|
};
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
Ok(w) => w,
|
|
Err(e) => {
|
|
print_error(format!("Failed to lock wallet: {e}"));
|
|
return WalletFfiError::InternalError;
|
|
}
|
|
};
|
|
|
|
match wallet.store_persistent_data() {
|
|
Ok(()) => WalletFfiError::Success,
|
|
Err(e) => {
|
|
print_error(format!("Failed to save wallet: {e}"));
|
|
WalletFfiError::StorageError
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Restore wallet data from mnemonic and password.
|
|
///
|
|
/// # Parameters
|
|
/// - `handle`: Valid wallet handle
|
|
/// - `mnemonic`: Valid pointer to instance of `FfiMnemonic`, provided by `wallet_ffi_create_new`
|
|
/// - `password`: Valid pointer to C string.
|
|
///
|
|
/// # Returns
|
|
/// - `Success` on successful restoration
|
|
/// - Error code on failure
|
|
///
|
|
/// # Safety
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
/// - `mnemonic` must be a valid pointer to instance of `FfiMnemonic`, provided by
|
|
/// `wallet_ffi_create_new`
|
|
/// - `password` must be a valid pointer to C string.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_restore_data(
|
|
handle: *mut WalletHandle,
|
|
mnemonic: *const c_char,
|
|
password: *const c_char,
|
|
) -> WalletFfiError {
|
|
let wrapper = match get_wallet(handle) {
|
|
Ok(w) => w,
|
|
Err(e) => return e,
|
|
};
|
|
|
|
let mut wallet = match wrapper.core.lock() {
|
|
Ok(w) => w,
|
|
Err(e) => {
|
|
print_error(format!("Failed to lock wallet: {e}"));
|
|
return WalletFfiError::InternalError;
|
|
}
|
|
};
|
|
|
|
let Ok(password) = c_str_to_string(password, "password") else {
|
|
return WalletFfiError::NullPointer;
|
|
};
|
|
|
|
let Ok(mnemonic) = c_str_to_string(mnemonic, "mnemonic") else {
|
|
return WalletFfiError::NullPointer;
|
|
};
|
|
|
|
match wallet.restore_storage(&mnemonic, &password) {
|
|
Ok(()) => WalletFfiError::Success,
|
|
Err(e) => {
|
|
print_error(format!("Failed to restore wallet data: {e}"));
|
|
WalletFfiError::StorageError
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the sequencer address from the wallet configuration.
|
|
///
|
|
/// # Parameters
|
|
/// - `handle`: Valid wallet handle
|
|
///
|
|
/// # Returns
|
|
/// - Pointer to null-terminated string on success (caller must free with
|
|
/// `wallet_ffi_free_string()`)
|
|
/// - Null pointer on error
|
|
///
|
|
/// # Safety
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_get_sequencer_addr(handle: *mut WalletHandle) -> *mut c_char {
|
|
let Ok(wrapper) = get_wallet(handle) else {
|
|
return ptr::null_mut();
|
|
};
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
Ok(w) => w,
|
|
Err(e) => {
|
|
print_error(format!("Failed to lock wallet: {e}"));
|
|
return ptr::null_mut();
|
|
}
|
|
};
|
|
|
|
let addr = wallet.config().sequencer_addr.clone().to_string();
|
|
|
|
match std::ffi::CString::new(addr) {
|
|
Ok(s) => s.into_raw(),
|
|
Err(e) => {
|
|
print_error(format!("Invalid sequencer address: {e}"));
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Free a string returned by wallet FFI functions.
|
|
///
|
|
/// # Safety
|
|
/// The pointer must be either null or a valid string returned by an FFI function.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn wallet_ffi_free_string(ptr: *mut c_char) {
|
|
if !ptr.is_null() {
|
|
unsafe {
|
|
drop(std::ffi::CString::from_raw(ptr));
|
|
}
|
|
}
|
|
}
|