fix(wallet_ffi): suggestion fix

This commit is contained in:
Pravdyvy 2026-06-23 11:10:53 +03:00
parent ca64789a90
commit 5c28acc961
7 changed files with 71 additions and 61 deletions

1
Cargo.lock generated
View File

@ -10765,6 +10765,7 @@ dependencies = [
name = "wallet-ffi"
version = "0.1.0"
dependencies = [
"bip39",
"cbindgen",
"common",
"key_protocol",

View File

@ -33,7 +33,7 @@ use wallet_ffi::{
FfiAccount, FfiAccountIdentity, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys,
FfiPublicAccountKey, FfiTransferResult, FfiU128, WalletHandle, error,
generic_transaction::{FfiProgramWithDependencies, FfiTransactionResult},
wallet::FfiCreateWalletResult,
wallet::FfiCreateWalletOutput,
};
unsafe extern "C" {
@ -41,7 +41,7 @@ unsafe extern "C" {
config_path: *const c_char,
storage_path: *const c_char,
password: *const c_char,
) -> FfiCreateWalletResult;
) -> FfiCreateWalletOutput;
fn wallet_ffi_open(
config_path: *const c_char,
@ -241,7 +241,7 @@ unsafe extern "C" {
fn new_wallet_ffi_with_test_context_config(
ctx: &BlockingTestContext,
home: &Path,
) -> Result<FfiCreateWalletResult> {
) -> Result<FfiCreateWalletOutput> {
let config_path = home.join("wallet_config.json");
let storage_path = home.join("storage.json");
let mut config = ctx.ctx().wallet().config().to_owned();
@ -319,7 +319,7 @@ fn new_wallet_ffi_with_test_context_config(
Ok(create_wallet_result)
}
fn new_wallet_ffi_with_default_config(password: &str) -> Result<FfiCreateWalletResult> {
fn new_wallet_ffi_with_default_config(password: &str) -> Result<FfiCreateWalletOutput> {
let tempdir = tempdir()?;
let config_path = tempdir.path().join("wallet_config.json");
let storage_path = tempdir.path().join("storage.json");
@ -356,7 +356,7 @@ fn wallet_ffi_create_public_accounts() -> Result<()> {
let new_public_account_ids_ffi = unsafe {
let mut account_ids = Vec::new();
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_default_config(password)?;
@ -395,7 +395,7 @@ fn wallet_ffi_create_private_accounts() -> Result<()> {
let new_npks_ffi = unsafe {
let mut npks = Vec::new();
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_default_config(password)?;
@ -427,7 +427,7 @@ fn wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
let home = tempfile::tempdir()?;
// Create a receiving key and save
let first_npk = unsafe {
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -467,7 +467,7 @@ fn test_wallet_ffi_list_accounts() -> Result<()> {
// Create the wallet FFI and track which account IDs were created as public/private
let (wallet_ffi_handle, created_public_ids) = unsafe {
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: handle,
mnemonic: _,
} = new_wallet_ffi_with_default_config(password)?;
@ -535,7 +535,7 @@ fn test_wallet_ffi_get_balance_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -568,7 +568,7 @@ fn test_wallet_ffi_get_account_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -608,7 +608,7 @@ fn test_wallet_ffi_get_account_private() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let account_id: AccountId = ctx.ctx().existing_private_accounts()[0];
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -647,7 +647,7 @@ fn test_wallet_ffi_get_public_account_keys() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -689,7 +689,7 @@ fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let account_id: AccountId = ctx.ctx().existing_private_accounts()[0];
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -774,7 +774,7 @@ fn wallet_ffi_base58_to_account_id() -> Result<()> {
fn wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -840,7 +840,7 @@ fn wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
fn wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -900,7 +900,7 @@ fn wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
fn test_wallet_ffi_transfer_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -957,7 +957,7 @@ fn test_wallet_ffi_transfer_public() -> Result<()> {
fn test_wallet_ffi_transfer_shielded() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -1036,7 +1036,7 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -1099,7 +1099,7 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
fn test_wallet_ffi_transfer_private() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -1178,11 +1178,13 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
fn restore_keys_from_seed_ffi() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
let mnemonic = unsafe { CString::from_raw(mnemonic) };
// Create 2 new private accounts
let (private_account_id_1, private_account_1_keys) = unsafe {
let mut out_keys = FfiPrivateAccountKeys::default();
@ -1394,7 +1396,7 @@ fn restore_keys_from_seed_ffi() -> Result<()> {
assert_eq!(public_account_id_2_balance, 103);
unsafe {
wallet_ffi_restore_data(wallet_ffi_handle, *mnemonic, password.as_ptr()).unwrap();
wallet_ffi_restore_data(wallet_ffi_handle, mnemonic.as_ptr(), password.as_ptr()).unwrap();
}
// Sync private account local storage with onchain encrypted state
@ -1464,7 +1466,7 @@ fn restore_keys_from_seed_ffi() -> Result<()> {
fn test_wallet_ffi_bridge_withdraw() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -1527,7 +1529,7 @@ fn test_wallet_ffi_bridge_withdraw() -> Result<()> {
fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
@ -1622,7 +1624,7 @@ fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
fn test_wallet_ffi_transfer_generic_private() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletResult {
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
mnemonic: _,
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;

View File

@ -21,6 +21,7 @@ tokio.workspace = true
key_protocol.workspace = true
serde_json.workspace = true
risc0-zkvm.workspace = true
bip39.workspace = true
[build-dependencies]
cbindgen = "0.29"

View File

@ -4,9 +4,11 @@ use std::{
ffi::{c_char, CStr, CString},
path::PathBuf,
ptr,
str::FromStr as _,
sync::Mutex,
};
use bip39::Mnemonic;
use wallet::{cli::execute_keys_restoration, WalletCore};
use crate::{
@ -21,13 +23,13 @@ pub(crate) struct WalletWrapper {
}
#[repr(C)]
pub struct FfiCreateWalletResult {
pub struct FfiCreateWalletOutput {
pub wallet: *mut WalletHandle,
/// C compatible(null terminated) string.
pub mnemonic: *mut *const c_char,
pub mnemonic: *mut c_char,
}
impl Default for FfiCreateWalletResult {
impl Default for FfiCreateWalletOutput {
fn default() -> Self {
Self {
wallet: std::ptr::null_mut(),
@ -97,17 +99,17 @@ pub unsafe extern "C" fn wallet_ffi_create_new(
config_path: *const c_char,
storage_path: *const c_char,
password: *const c_char,
) -> FfiCreateWalletResult {
) -> FfiCreateWalletOutput {
let Ok(config_path) = c_str_to_path(config_path, "config_path") else {
return FfiCreateWalletResult::default();
return FfiCreateWalletOutput::default();
};
let Ok(storage_path) = c_str_to_path(storage_path, "storage_path") else {
return FfiCreateWalletResult::default();
return FfiCreateWalletOutput::default();
};
let Ok(password) = c_str_to_string(password, "password") else {
return FfiCreateWalletResult::default();
return FfiCreateWalletOutput::default();
};
match WalletCore::new_init_storage(config_path, storage_path, None, &password) {
@ -118,21 +120,19 @@ pub unsafe extern "C" fn wallet_ffi_create_new(
let handle = Box::into_raw(wrapper).cast::<WalletHandle>();
let Ok(c_mnemonic_string) = CString::new(mnemonic.to_string()) else {
return FfiCreateWalletResult::default();
return FfiCreateWalletOutput::default();
};
let raw_pointer = CString::into_raw(c_mnemonic_string);
let boxed_mnemonic_string = Box::new(raw_pointer.cast_const());
let raw_mnemonic_string_pointer = Box::into_raw(boxed_mnemonic_string);
FfiCreateWalletResult {
FfiCreateWalletOutput {
wallet: handle,
mnemonic: raw_mnemonic_string_pointer,
mnemonic: raw_pointer,
}
}
Err(e) => {
print_error(format!("Failed to create wallet: {e}"));
FfiCreateWalletResult::default()
FfiCreateWalletOutput::default()
}
}
}
@ -237,8 +237,9 @@ pub unsafe extern "C" fn wallet_ffi_save(handle: *mut WalletHandle) -> WalletFfi
///
/// # Parameters
/// - `handle`: Valid wallet handle
/// - `mnemonic`: Valid pointer to instance of `FfiMnemonic`, provided by `wallet_ffi_create_new`
/// - `mnemonic`: Valid pointer to instance of `* char`, provided by `wallet_ffi_create_new`
/// - `password`: Valid pointer to C string.
/// - `depth`: Depth of a reconstructed tree
///
/// # Returns
/// - `Success` on successful restoration
@ -246,14 +247,16 @@ pub unsafe extern "C" fn wallet_ffi_save(handle: *mut WalletHandle) -> WalletFfi
///
/// # 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
/// - `mnemonic` must be a valid pointer to instance of `* char`, provided by
/// `wallet_ffi_create_new`
/// - `password` must be a valid pointer to C string.
/// - `depth` parameter induces exponential growth in execution time, be aware of it.
#[no_mangle]
pub unsafe extern "C" fn wallet_ffi_restore_data(
handle: *mut WalletHandle,
mnemonic: *const c_char,
password: *const c_char,
depth: u32,
) -> WalletFfiError {
let wrapper = match get_wallet(handle) {
Ok(w) => w,
@ -276,6 +279,14 @@ pub unsafe extern "C" fn wallet_ffi_restore_data(
return WalletFfiError::NullPointer;
};
let mnemonic = match Mnemonic::from_str(&mnemonic) {
Ok(mn) => mn,
Err(e) => {
print_error(format!("Failed to parse mnemonic: {e}"));
return WalletFfiError::SerializationError;
}
};
let res = match wallet.restore_storage(&mnemonic, &password) {
Ok(()) => WalletFfiError::Success,
Err(e) => {
@ -285,7 +296,7 @@ pub unsafe extern "C" fn wallet_ffi_restore_data(
};
if res == WalletFfiError::Success {
match block_on(execute_keys_restoration(&mut wallet, 10)) {
match block_on(execute_keys_restoration(&mut wallet, depth)) {
Ok(()) => WalletFfiError::Success,
Err(err) => {
print_error(format!("Failed to restore wallet data: {err}"));

View File

@ -299,13 +299,13 @@ typedef struct FfiPublicAccountKey {
struct FfiBytes32 public_key;
} FfiPublicAccountKey;
typedef struct FfiCreateWalletResult {
typedef struct FfiCreateWalletOutput {
struct WalletHandle *wallet;
/**
* C compatible(null terminated) string.
*/
const char **mnemonic;
} FfiCreateWalletResult;
char *mnemonic;
} FfiCreateWalletOutput;
/**
* Create a new public account.
@ -1387,7 +1387,7 @@ void wallet_ffi_free_transfer_result(struct FfiTransferResult *result);
* # Safety
* All string parameters must be valid null-terminated UTF-8 strings.
*/
struct FfiCreateWalletResult wallet_ffi_create_new(const char *config_path,
struct FfiCreateWalletOutput wallet_ffi_create_new(const char *config_path,
const char *storage_path,
const char *password);
@ -1444,8 +1444,9 @@ enum WalletFfiError wallet_ffi_save(struct WalletHandle *handle);
*
* # Parameters
* - `handle`: Valid wallet handle
* - `mnemonic`: Valid pointer to instance of `FfiMnemonic`, provided by `wallet_ffi_create_new`
* - `mnemonic`: Valid pointer to instance of `* char`, provided by `wallet_ffi_create_new`
* - `password`: Valid pointer to C string.
* - `depth`: Depth of a reconstructed tree
*
* # Returns
* - `Success` on successful restoration
@ -1453,13 +1454,15 @@ enum WalletFfiError wallet_ffi_save(struct WalletHandle *handle);
*
* # 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
* - `mnemonic` must be a valid pointer to instance of `* char`, provided by
* `wallet_ffi_create_new`
* - `password` must be a valid pointer to C string.
* - `depth` parameter induces exponential growth in execution time, be aware of it.
*/
enum WalletFfiError wallet_ffi_restore_data(struct WalletHandle *handle,
const char *mnemonic,
const char *password);
const char *password,
uint32_t depth);
/**
* Get the sequencer address from the wallet configuration.

View File

@ -274,7 +274,7 @@ pub async fn execute_subcommand(
Command::RestoreKeys { depth } => {
let mnemonic = read_mnemonic_from_stdin()?;
let password = read_password_from_stdin()?;
wallet_core.restore_storage(&mnemonic.to_string(), &password)?;
wallet_core.restore_storage(&mnemonic, &password)?;
execute_keys_restoration(wallet_core, depth).await?;
SubcommandReturnValue::Empty

View File

@ -199,8 +199,8 @@ impl WalletCore {
}
/// Restore storage from an existing mnemonic phrase.
pub fn restore_storage(&mut self, mnemonic: &str, password: &str) -> Result<()> {
self.storage.restore(&Mnemonic::parse(mnemonic)?, password)
pub fn restore_storage(&mut self, mnemonic: &Mnemonic, password: &str) -> Result<()> {
self.storage.restore(mnemonic, password)
}
/// Store persistent data at home.
@ -875,10 +875,7 @@ impl WalletCore {
#[cfg(test)]
mod tests {
use std::{
ffi::{CStr, CString},
str::FromStr as _,
};
use std::{ffi::CString, str::FromStr as _};
use bip39::Mnemonic;
@ -888,14 +885,9 @@ mod tests {
Mnemonic::from_entropy(&[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]).unwrap();
let c_mnemonic_string = CString::new(mnemonic.to_string()).unwrap();
let boxed_mnemonic_string = Box::new(c_mnemonic_string.as_ptr());
let raw_mnemonic_string_pointer = Box::into_raw(boxed_mnemonic_string);
let c_mnemonic_string_raw = c_mnemonic_string.into_raw();
// Safety: Will be safe, pointer is created from CString
let c_str_pointer = unsafe { *raw_mnemonic_string_pointer };
// Safety: Will be safe, pointer is created from CString
let c_str = unsafe { CStr::from_ptr(c_str_pointer) };
let c_str = unsafe { CString::from_raw(c_mnemonic_string_raw) };
let mn_string = c_str.to_str().unwrap();
let mn_ret = Mnemonic::from_str(mn_string).unwrap();