diff --git a/lez/wallet-ffi/src/wallet.rs b/lez/wallet-ffi/src/wallet.rs index b19aaae5..324d5a8e 100644 --- a/lez/wallet-ffi/src/wallet.rs +++ b/lez/wallet-ffi/src/wallet.rs @@ -178,6 +178,192 @@ pub unsafe extern "C" fn wallet_ffi_open( } } +/// Resolve LEZ's canonical wallet paths (`LEE_WALLET_HOME_DIR` or `~/.lee/wallet`) +/// and ensure the home directory exists. Centralizes the path layout so callers +/// don't have to reconstruct it. +fn default_wallet_paths() -> Result<(PathBuf, PathBuf), WalletFfiError> { + let home = wallet::helperfunctions::get_home().map_err(|e| { + print_error(format!("Failed to resolve wallet home: {e}")); + WalletFfiError::InternalError + })?; + + if let Err(e) = std::fs::create_dir_all(&home) { + print_error(format!( + "Failed to create wallet home {}: {e}", + home.display() + )); + return Err(WalletFfiError::StorageError); + } + + let config_path = wallet::helperfunctions::fetch_config_path().map_err(|e| { + print_error(format!("Failed to resolve config path: {e}")); + WalletFfiError::InternalError + })?; + let storage_path = wallet::helperfunctions::fetch_persistent_storage_path().map_err(|e| { + print_error(format!("Failed to resolve storage path: {e}")); + WalletFfiError::InternalError + })?; + + Ok((config_path, storage_path)) +} + +/// Create a new wallet at LEZ's canonical home, deriving the paths from the +/// environment (`LEE_WALLET_HOME_DIR` or `~/.lee/wallet`) and creating the +/// directory if needed. +/// +/// This is the path-free equivalent of [`wallet_ffi_create_new`]: callers that +/// just want the default location don't have to reconstruct LEZ's path layout. +/// +/// # Parameters +/// - `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 +/// `password` must be a valid null-terminated UTF-8 string. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_create_new_default( + password: *const c_char, +) -> FfiCreateWalletOutput { + let Ok(password) = c_str_to_string(password, "password") else { + return FfiCreateWalletOutput::default(); + }; + + let Ok((config_path, storage_path)) = default_wallet_paths() else { + return FfiCreateWalletOutput::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::(); + + let Ok(c_mnemonic_string) = CString::new(mnemonic.to_string()) else { + return FfiCreateWalletOutput::default(); + }; + + let raw_pointer = CString::into_raw(c_mnemonic_string); + + FfiCreateWalletOutput { + wallet: handle, + mnemonic: raw_pointer, + } + } + Err(e) => { + print_error(format!("Failed to create wallet: {e}")); + FfiCreateWalletOutput::default() + } + } +} + +/// Open the existing wallet at LEZ's canonical home, deriving the paths from the +/// environment (`LEE_WALLET_HOME_DIR` or `~/.lee/wallet`). +/// +/// This is the path-free equivalent of [`wallet_ffi_open`]. +/// +/// # Returns +/// - Opaque wallet handle on success +/// - Null pointer on error (call `wallet_ffi_get_last_error()` for details) +/// +/// # Safety +/// This function takes no pointer arguments and is always safe to call. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_open_default() -> *mut WalletHandle { + match WalletCore::from_env() { + Ok(core) => { + let wrapper = Box::new(WalletWrapper { + core: Mutex::new(core), + }); + Box::into_raw(wrapper).cast::() + } + Err(e) => { + print_error(format!("Failed to open wallet: {e}")); + ptr::null_mut() + } + } +} + +/// Convert a resolved path into an owned C string for return across the FFI. +fn path_to_c_string(path: PathBuf) -> *mut c_char { + match CString::new(path.to_string_lossy().into_owned()) { + Ok(s) => s.into_raw(), + Err(e) => { + print_error(format!("Invalid path string: {e}")); + ptr::null_mut() + } + } +} + +/// Return LEZ's canonical wallet config path (`LEE_WALLET_HOME_DIR` or +/// `~/.lee/wallet`, plus `wallet_config.json`). +/// +/// Lets callers display or inspect the default location without reconstructing +/// LEZ's path layout themselves. +/// +/// # Returns +/// - Pointer to null-terminated string on success (caller must free with +/// `wallet_ffi_free_string()`) +/// - Null pointer on error +/// +/// # Safety +/// This function takes no pointer arguments and is always safe to call. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_default_config_path() -> *mut c_char { + match wallet::helperfunctions::fetch_config_path() { + Ok(path) => path_to_c_string(path), + Err(e) => { + print_error(format!("Failed to resolve config path: {e}")); + ptr::null_mut() + } + } +} + +/// Return LEZ's canonical wallet storage path (`LEE_WALLET_HOME_DIR` or +/// `~/.lee/wallet`, plus `storage.json`). +/// +/// # Returns +/// - Pointer to null-terminated string on success (caller must free with +/// `wallet_ffi_free_string()`) +/// - Null pointer on error +/// +/// # Safety +/// This function takes no pointer arguments and is always safe to call. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_default_storage_path() -> *mut c_char { + match wallet::helperfunctions::fetch_persistent_storage_path() { + Ok(path) => path_to_c_string(path), + Err(e) => { + print_error(format!("Failed to resolve storage path: {e}")); + ptr::null_mut() + } + } +} + +/// Whether a wallet already exists at LEZ's canonical home (i.e. its +/// `storage.json` is present). Lets callers decide between an open and a +/// create flow without touching the filesystem or knowing the path. +/// +/// # Returns +/// - `true` if the default storage file exists, `false` otherwise (including +/// when the path can't be resolved) +/// +/// # Safety +/// This function takes no pointer arguments and is always safe to call. +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_wallet_exists_default() -> bool { + match wallet::helperfunctions::fetch_persistent_storage_path() { + Ok(path) => path.exists(), + Err(e) => { + print_error(format!("Failed to resolve storage path: {e}")); + false + } + } +} + /// Destroy a wallet handle and free its resources. /// /// After calling this function, the handle is invalid and must not be used. diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index d83c520e..b6a0d2bb 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -1488,6 +1488,86 @@ struct FfiCreateWalletOutput wallet_ffi_create_new(const char *config_path, */ struct WalletHandle *wallet_ffi_open(const char *config_path, const char *storage_path); +/** + * Create a new wallet at LEZ's canonical home, deriving the paths from the + * environment (`LEE_WALLET_HOME_DIR` or `~/.lee/wallet`) and creating the + * directory if needed. + * + * This is the path-free equivalent of [`wallet_ffi_create_new`]: callers that + * just want the default location don't have to reconstruct LEZ's path layout. + * + * # Parameters + * - `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 + * `password` must be a valid null-terminated UTF-8 string. + */ +struct FfiCreateWalletOutput wallet_ffi_create_new_default(const char *password); + +/** + * Open the existing wallet at LEZ's canonical home, deriving the paths from the + * environment (`LEE_WALLET_HOME_DIR` or `~/.lee/wallet`). + * + * This is the path-free equivalent of [`wallet_ffi_open`]. + * + * # Returns + * - Opaque wallet handle on success + * - Null pointer on error (call `wallet_ffi_get_last_error()` for details) + * + * # Safety + * This function takes no pointer arguments and is always safe to call. + */ +struct WalletHandle *wallet_ffi_open_default(void); + +/** + * Return LEZ's canonical wallet config path (`LEE_WALLET_HOME_DIR` or + * `~/.lee/wallet`, plus `wallet_config.json`). + * + * Lets callers display or inspect the default location without reconstructing + * LEZ's path layout themselves. + * + * # Returns + * - Pointer to null-terminated string on success (caller must free with + * `wallet_ffi_free_string()`) + * - Null pointer on error + * + * # Safety + * This function takes no pointer arguments and is always safe to call. + */ +char *wallet_ffi_default_config_path(void); + +/** + * Return LEZ's canonical wallet storage path (`LEE_WALLET_HOME_DIR` or + * `~/.lee/wallet`, plus `storage.json`). + * + * # Returns + * - Pointer to null-terminated string on success (caller must free with + * `wallet_ffi_free_string()`) + * - Null pointer on error + * + * # Safety + * This function takes no pointer arguments and is always safe to call. + */ +char *wallet_ffi_default_storage_path(void); + +/** + * Whether a wallet already exists at LEZ's canonical home (i.e. its + * `storage.json` is present). Lets callers decide between an open and a + * create flow without touching the filesystem or knowing the path. + * + * # Returns + * - `true` if the default storage file exists, `false` otherwise (including + * when the path can't be resolved) + * + * # Safety + * This function takes no pointer arguments and is always safe to call. + */ +bool wallet_ffi_wallet_exists_default(void); + /** * Destroy a wallet handle and free its resources. *