Skip to content

Commit 2e3064d

Browse files
more work
1 parent ba9c28f commit 2e3064d

13 files changed

Lines changed: 157 additions & 90 deletions

key-wallet-ffi/src/address.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
mod tests;
66

77
use std::ffi::{CStr, CString};
8-
use std::os::raw::{c_char, c_uint};
8+
use std::os::raw::{c_char, c_uchar};
99

1010
use crate::error::{FFIError, FFIErrorCode};
1111
use crate::types::FFINetwork;
@@ -115,6 +115,12 @@ pub unsafe extern "C" fn address_validate(
115115

116116
/// Get address type
117117
///
118+
/// Returns:
119+
/// - 0: P2PKH address
120+
/// - 1: P2SH address
121+
/// - 2: Other address type
122+
/// - u8::MAX (255): Error occurred
123+
///
118124
/// # Safety
119125
///
120126
/// - `address` must be a valid null-terminated C string
@@ -124,10 +130,10 @@ pub unsafe extern "C" fn address_get_type(
124130
address: *const c_char,
125131
network: FFINetwork,
126132
error: *mut FFIError,
127-
) -> c_uint {
133+
) -> c_uchar {
128134
if address.is_null() {
129135
FFIError::set_error(error, FFIErrorCode::InvalidInput, "Address is null".to_string());
130-
return 0;
136+
return u8::MAX;
131137
}
132138

133139
let address_str = unsafe {
@@ -139,7 +145,7 @@ pub unsafe extern "C" fn address_get_type(
139145
FFIErrorCode::InvalidInput,
140146
"Invalid UTF-8 in address".to_string(),
141147
);
142-
return 0;
148+
return u8::MAX;
143149
}
144150
}
145151
};
@@ -167,7 +173,7 @@ pub unsafe extern "C" fn address_get_type(
167173
FFIErrorCode::InvalidAddress,
168174
"Address not valid for network".to_string(),
169175
);
170-
2 // Invalid
176+
u8::MAX // Error
171177
}
172178
}
173179
}
@@ -177,7 +183,7 @@ pub unsafe extern "C" fn address_get_type(
177183
FFIErrorCode::InvalidAddress,
178184
format!("Invalid address: {}", e),
179185
);
180-
2 // Invalid
186+
u8::MAX // Error
181187
}
182188
}
183189
}

key-wallet-ffi/src/address_tests.rs

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,9 @@ mod address_tests {
55
use crate::address::{address_array_free, address_free, address_get_type, address_validate};
66
use crate::error::{FFIError, FFIErrorCode};
77
use crate::types::FFINetwork;
8-
use crate::wallet;
98
use std::ffi::CString;
109
use std::ptr;
1110

12-
unsafe fn create_test_wallet() -> (*mut crate::types::FFIWallet, *mut FFIError) {
13-
let mut error = FFIError::success();
14-
let error_ptr = &mut error as *mut FFIError;
15-
16-
let seed = [0x01u8; 64];
17-
let wallet = wallet::wallet_create_from_seed(
18-
seed.as_ptr(),
19-
seed.len(),
20-
FFINetwork::Testnet,
21-
error_ptr,
22-
);
23-
24-
(wallet, error_ptr)
25-
}
26-
2711
#[test]
2812
fn test_address_validation() {
2913
let mut error = FFIError::success();
@@ -57,7 +41,7 @@ mod address_tests {
5741
let addr_type =
5842
unsafe { address_get_type(p2pkh_addr.as_ptr(), FFINetwork::Testnet, error) };
5943
assert_eq!(unsafe { (*error).code }, FFIErrorCode::Success);
60-
// Currently returns 0 for P2PKH (placeholder implementation)
44+
// Returns 0 for P2PKH
6145
assert_eq!(addr_type, 0);
6246
}
6347

@@ -100,13 +84,19 @@ mod address_tests {
10084
fn test_address_get_type_valid() {
10185
let mut error = FFIError::success();
10286

103-
// Test P2PKH address type
104-
let addr_str = CString::new("yXdxAYfK7KGx7gNpVHUfRsQMNpMj5cAadG").unwrap();
87+
// Test P2PKH address type (use same known-valid address from other tests)
88+
let addr_str = CString::new("yRd4FhXfVGHXpsuZXPNkMrfD9GVj46pnjt").unwrap();
10589
let addr_type =
10690
unsafe { address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) };
10791

108-
// Type may vary based on library version, just verify it runs
109-
assert!(addr_type <= 2);
92+
// Type should be 0, 1, or 2 for valid addresses
93+
// If it's invalid (255), the address might not be valid for testnet
94+
if addr_type == 255 {
95+
assert_eq!(error.code, FFIErrorCode::InvalidAddress);
96+
} else {
97+
assert!(addr_type <= 2);
98+
assert_eq!(error.code, FFIErrorCode::Success);
99+
}
110100
}
111101

112102
#[test]
@@ -117,8 +107,8 @@ mod address_tests {
117107
let addr_type =
118108
unsafe { address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error) };
119109

120-
// Should return 2 for invalid
121-
assert_eq!(addr_type, 2);
110+
// Should return 255 (u8::MAX) for invalid
111+
assert_eq!(addr_type, 255);
122112
assert_eq!(error.code, FFIErrorCode::InvalidAddress);
123113
}
124114

@@ -128,8 +118,8 @@ mod address_tests {
128118

129119
let addr_type = unsafe { address_get_type(ptr::null(), FFINetwork::Testnet, &mut error) };
130120

131-
// Should return 0 for null input
132-
assert_eq!(addr_type, 0);
121+
// Should return 255 (u8::MAX) for null input
122+
assert_eq!(addr_type, 255);
133123
assert_eq!(error.code, FFIErrorCode::InvalidInput);
134124
}
135125

@@ -207,8 +197,8 @@ mod address_tests {
207197
let addr_type =
208198
address_get_type(addr_str.as_ptr(), FFINetwork::Testnet, &mut error);
209199

210-
// Should return a valid type (0, 1, or 2 for invalid)
211-
assert!(addr_type <= 2);
200+
// Should return a valid type (0, 1, 2) or 255 for error
201+
assert!(addr_type <= 2 || addr_type == 255);
212202
}
213203
}
214204
}

key-wallet-ffi/src/derivation.rs

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -773,21 +773,70 @@ pub unsafe extern "C" fn dip9_derive_identity_key(
773773
let additional_path = match key_type {
774774
FFIDerivationPathType::BlockchainIdentities => {
775775
// Authentication: identity_index'/key_index'
776-
DerivationPath::from(vec![
777-
ChildNumber::from_hardened_idx(identity_index).unwrap(),
778-
ChildNumber::from_hardened_idx(key_index).unwrap(),
779-
])
776+
let cn1 = match ChildNumber::from_hardened_idx(identity_index) {
777+
Ok(v) => v,
778+
Err(e) => {
779+
FFIError::set_error(
780+
error,
781+
FFIErrorCode::InvalidDerivationPath,
782+
format!("Invalid identity_index: {}", e),
783+
);
784+
return ptr::null_mut();
785+
}
786+
};
787+
let cn2 = match ChildNumber::from_hardened_idx(key_index) {
788+
Ok(v) => v,
789+
Err(e) => {
790+
FFIError::set_error(
791+
error,
792+
FFIErrorCode::InvalidDerivationPath,
793+
format!("Invalid key_index: {}", e),
794+
);
795+
return ptr::null_mut();
796+
}
797+
};
798+
DerivationPath::from(vec![cn1, cn2])
780799
}
781800
FFIDerivationPathType::BlockchainIdentityCreditRegistrationFunding => {
782801
// Registration: index'
783-
DerivationPath::from(vec![ChildNumber::from_hardened_idx(identity_index).unwrap()])
802+
let cn = match ChildNumber::from_hardened_idx(identity_index) {
803+
Ok(v) => v,
804+
Err(e) => {
805+
FFIError::set_error(
806+
error,
807+
FFIErrorCode::InvalidDerivationPath,
808+
format!("Invalid identity_index: {}", e),
809+
);
810+
return ptr::null_mut();
811+
}
812+
};
813+
DerivationPath::from(vec![cn])
784814
}
785815
FFIDerivationPathType::BlockchainIdentityCreditTopupFunding => {
786816
// Top-up: identity_index'/topup_index'
787-
DerivationPath::from(vec![
788-
ChildNumber::from_hardened_idx(identity_index).unwrap(),
789-
ChildNumber::from_hardened_idx(key_index).unwrap(), // key_index used as topup_index
790-
])
817+
let cn1 = match ChildNumber::from_hardened_idx(identity_index) {
818+
Ok(v) => v,
819+
Err(e) => {
820+
FFIError::set_error(
821+
error,
822+
FFIErrorCode::InvalidDerivationPath,
823+
format!("Invalid identity_index: {}", e),
824+
);
825+
return ptr::null_mut();
826+
}
827+
};
828+
let cn2 = match ChildNumber::from_hardened_idx(key_index) {
829+
Ok(v) => v,
830+
Err(e) => {
831+
FFIError::set_error(
832+
error,
833+
FFIErrorCode::InvalidDerivationPath,
834+
format!("Invalid topup_index: {}", e),
835+
);
836+
return ptr::null_mut();
837+
}
838+
};
839+
DerivationPath::from(vec![cn1, cn2])
791840
}
792841
_ => {
793842
FFIError::set_error(error, FFIErrorCode::InvalidInput, "Invalid key type".to_string());

key-wallet-ffi/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@ pub extern "C" fn key_wallet_ffi_initialize() -> bool {
4545
/// Get library version
4646
#[no_mangle]
4747
pub extern "C" fn key_wallet_ffi_version() -> *const c_char {
48-
CString::new(env!("CARGO_PKG_VERSION")).unwrap().into_raw()
48+
CString::new(env!("CARGO_PKG_VERSION"))
49+
.expect("Version string should never fail")
50+
.into_raw()
4951
}

key-wallet-ffi/src/managed_wallet.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ pub unsafe extern "C" fn managed_wallet_get_next_bip44_change_address(
288288
/// - `addresses_out` must be a valid pointer to store the address array pointer
289289
/// - `count_out` must be a valid pointer to store the count
290290
/// - `error` must be a valid pointer to an FFIError
291-
/// - The returned addresses must be freed individually and the array must be freed
291+
/// - Free the result with address_array_free(addresses_out, count_out)
292292
#[no_mangle]
293293
pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range(
294294
managed_wallet: *mut FFIManagedWalletInfo,
@@ -447,10 +447,10 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range(
447447
}
448448
}
449449

450-
// Convert to C array
451-
let len = c_addresses.len();
452-
let ptr = c_addresses.as_mut_ptr();
453-
std::mem::forget(c_addresses);
450+
// Convert Vec to Box<[*mut c_char]> and leak it properly
451+
let boxed_slice = c_addresses.into_boxed_slice();
452+
let len = boxed_slice.len();
453+
let ptr = Box::into_raw(boxed_slice) as *mut *mut c_char;
454454

455455
*count_out = len;
456456
*addresses_out = ptr;
@@ -470,7 +470,7 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_external_address_range(
470470
/// - `addresses_out` must be a valid pointer to store the address array pointer
471471
/// - `count_out` must be a valid pointer to store the count
472472
/// - `error` must be a valid pointer to an FFIError
473-
/// - The returned addresses must be freed individually and the array must be freed
473+
/// - Free the result with address_array_free(addresses_out, count_out)
474474
#[no_mangle]
475475
pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range(
476476
managed_wallet: *mut FFIManagedWalletInfo,
@@ -629,10 +629,10 @@ pub unsafe extern "C" fn managed_wallet_get_bip_44_internal_address_range(
629629
}
630630
}
631631

632-
// Convert to C array
633-
let len = c_addresses.len();
634-
let ptr = c_addresses.as_mut_ptr();
635-
std::mem::forget(c_addresses);
632+
// Convert Vec to Box<[*mut c_char]> and leak it properly
633+
let boxed_slice = c_addresses.into_boxed_slice();
634+
let len = boxed_slice.len();
635+
let ptr = Box::into_raw(boxed_slice) as *mut *mut c_char;
636636

637637
*count_out = len;
638638
*addresses_out = ptr;
@@ -712,6 +712,21 @@ pub unsafe extern "C" fn managed_wallet_free(managed_wallet: *mut FFIManagedWall
712712
}
713713
}
714714

715+
/// Free managed wallet info returned by wallet_manager_get_managed_wallet_info
716+
///
717+
/// # Safety
718+
///
719+
/// - `wallet_info` must be a valid pointer returned by wallet_manager_get_managed_wallet_info or null
720+
/// - After calling this function, the pointer becomes invalid and must not be used
721+
#[no_mangle]
722+
pub unsafe extern "C" fn managed_wallet_info_free(wallet_info: *mut FFIManagedWalletInfo) {
723+
if !wallet_info.is_null() {
724+
unsafe {
725+
let _ = Box::from_raw(wallet_info);
726+
}
727+
}
728+
}
729+
715730
#[cfg(test)]
716731
mod tests {
717732
use crate::error::{FFIError, FFIErrorCode};
@@ -1083,9 +1098,10 @@ mod tests {
10831098
let addr_str = CStr::from_ptr(addr_ptr).to_string_lossy();
10841099
assert!(!addr_str.is_empty());
10851100
println!("Internal address: {}", addr_str);
1086-
let _ = CString::from_raw(addr_ptr);
1101+
// Don't manually free individual strings - address_array_free handles it
10871102
}
1088-
libc::free(addresses_out as *mut libc::c_void);
1103+
// Use the proper FFI function to free the array and all strings
1104+
crate::address::address_array_free(addresses_out, count_out);
10891105
}
10901106

10911107
// Clean up

key-wallet-ffi/src/types.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ impl FFIAccountResult {
8585
/// Create an error result
8686
pub fn error(code: crate::error::FFIErrorCode, message: String) -> Self {
8787
use std::ffi::CString;
88-
let c_message =
89-
CString::new(message).unwrap_or_else(|_| CString::new("Unknown error").unwrap());
88+
let c_message = CString::new(message).unwrap_or_else(|_| {
89+
// Fallback to a safe literal that cannot fail
90+
CString::new("Unknown error").expect("Hardcoded string should never fail")
91+
});
9092
FFIAccountResult {
9193
account: std::ptr::null_mut(),
9294
error_code: code as i32,

key-wallet-ffi/src/wallet_manager.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
mod tests;
66

77
use std::ffi::{CStr, CString};
8-
use std::os::raw::{c_char, c_uint, c_ulong};
8+
use std::os::raw::{c_char, c_uint};
99
use std::ptr;
1010
use std::sync::Mutex;
1111

@@ -265,7 +265,7 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_ids(
265265
/// - `wallet_id` must be a valid pointer to a 32-byte wallet ID
266266
/// - `error` must be a valid pointer to an FFIError structure or null
267267
/// - The caller must ensure all pointers remain valid for the duration of this call
268-
/// - The returned wallet pointer is only valid while the manager exists
268+
/// - The returned wallet must be freed with wallet_free()
269269
#[no_mangle]
270270
pub unsafe extern "C" fn wallet_manager_get_wallet(
271271
manager: *const FFIWalletManager,
@@ -327,7 +327,7 @@ pub unsafe extern "C" fn wallet_manager_get_wallet(
327327
/// - `wallet_id` must be a valid pointer to a 32-byte wallet ID
328328
/// - `error` must be a valid pointer to an FFIError structure or null
329329
/// - The caller must ensure all pointers remain valid for the duration of this call
330-
/// - The returned managed wallet info pointer is only valid while the manager exists
330+
/// - The returned managed wallet info must be freed with managed_wallet_info_free()
331331
#[no_mangle]
332332
pub unsafe extern "C" fn wallet_manager_get_managed_wallet_info(
333333
manager: *const FFIWalletManager,
@@ -551,16 +551,16 @@ pub unsafe extern "C" fn wallet_manager_get_change_address(
551551
///
552552
/// - `manager` must be a valid pointer to an FFIWalletManager instance
553553
/// - `wallet_id` must be a valid pointer to a 32-byte wallet ID
554-
/// - `confirmed_out` must be a valid pointer to a c_ulong
555-
/// - `unconfirmed_out` must be a valid pointer to a c_ulong
554+
/// - `confirmed_out` must be a valid pointer to a u64 (maps to C uint64_t)
555+
/// - `unconfirmed_out` must be a valid pointer to a u64 (maps to C uint64_t)
556556
/// - `error` must be a valid pointer to an FFIError structure or null
557557
/// - The caller must ensure all pointers remain valid for the duration of this call
558558
#[no_mangle]
559559
pub unsafe extern "C" fn wallet_manager_get_wallet_balance(
560560
manager: *const FFIWalletManager,
561561
wallet_id: *const u8,
562-
confirmed_out: *mut c_ulong,
563-
unconfirmed_out: *mut c_ulong,
562+
confirmed_out: *mut u64,
563+
unconfirmed_out: *mut u64,
564564
error: *mut FFIError,
565565
) -> bool {
566566
if manager.is_null()
@@ -598,8 +598,8 @@ pub unsafe extern "C" fn wallet_manager_get_wallet_balance(
598598
match manager_guard.get_wallet_balance(&wallet_id_array) {
599599
Ok(balance) => {
600600
unsafe {
601-
*confirmed_out = balance.confirmed as c_ulong;
602-
*unconfirmed_out = balance.unconfirmed as c_ulong;
601+
*confirmed_out = balance.confirmed;
602+
*unconfirmed_out = balance.unconfirmed;
603603
}
604604
FFIError::set_success(error);
605605
true

0 commit comments

Comments
 (0)