From dfe2e33b42894f786f4ea4a9b88d6c85cedf5e09 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 5 Jul 2024 16:05:11 +0200 Subject: [PATCH 1/6] add ffi features --- .../common_types/src/tari_address/mod.rs | 9 +- base_layer/wallet_ffi/src/lib.rs | 117 +++++++++++++++++- base_layer/wallet_ffi/wallet.h | 34 +++++ 3 files changed, 153 insertions(+), 7 deletions(-) diff --git a/base_layer/common_types/src/tari_address/mod.rs b/base_layer/common_types/src/tari_address/mod.rs index 7312c0f707..2f2a19471b 100644 --- a/base_layer/common_types/src/tari_address/mod.rs +++ b/base_layer/common_types/src/tari_address/mod.rs @@ -68,6 +68,10 @@ impl TariAddressFeatures { pub fn create_interactive_and_one_sided() -> TariAddressFeatures { TariAddressFeatures::INTERACTIVE | TariAddressFeatures::ONE_SIDED } + + pub fn as_u8(&self) -> u8 { + self.0 + } } impl Default for TariAddressFeatures { @@ -179,9 +183,10 @@ impl TariAddress { } /// Gets the checksum from the Tari Address - pub fn checksum(&self) -> u8 { + pub fn calculate_checksum(&self) -> u8 { let bytes = self.to_vec(); - bytes[bytes.len()] + // -1 is safe as this the len will always be greater than 0 + bytes[bytes.len() - 1] } /// Convert Tari Address to an emoji string diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index fceedba2ad..35c6424c1e 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -118,7 +118,7 @@ use tari_common::{ network_check::set_network_if_choice_valid, }; use tari_common_types::{ - emoji::emoji_set, + emoji::{emoji_set, EMOJI}, tari_address::{TariAddress, TariAddressError}, transaction::{TransactionDirection, TransactionStatus, TxId}, types::{ComAndPubSignature, Commitment, PublicKey, RangeProof, SignatureWithDomain}, @@ -1305,7 +1305,10 @@ pub unsafe extern "C" fn tari_address_checksum_u8(address: *mut TariWalletAddres ptr::swap(error_out, &mut error as *mut c_int); return 0; } - address.as_ref().expect("Address should not be empty").checksum() + address + .as_ref() + .expect("Address should not be empty") + .calculate_checksum() } /// Creates a char array from a TariWalletAddress's features @@ -1331,15 +1334,43 @@ pub unsafe extern "C" fn tari_address_features(address: *mut TariWalletAddress, ptr::swap(error_out, &mut error as *mut c_int); return CString::into_raw(result); } - let network_string = address + let features_string = address .as_ref() .expect("Address should not be empty") .features() .to_string(); - result = CString::new(network_string).expect("string will not fail."); + result = CString::new(features_string).expect("string will not fail."); CString::into_raw(result) } +/// Creates a char array from a TariWalletAddress's features +/// +/// ## Arguments +/// `address` - The pointer to a TariWalletAddress +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// u8` - Returns u8 representing the features. On failure, returns 0. This may be valid so always check the error out +/// +/// # Safety +/// The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak +#[no_mangle] +pub unsafe extern "C" fn tari_address_features_u8(address: *mut TariWalletAddress, error_out: *mut c_int) -> u8 { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if address.is_null() { + error = LibWalletError::from(InterfaceError::NullError("address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + address + .as_ref() + .expect("Address should not be empty") + .features() + .as_u8() +} + /// Creates a public key from a TariWalletAddress's view key /// /// ## Arguments @@ -1446,6 +1477,26 @@ pub unsafe extern "C" fn emoji_id_to_tari_address( } } +/// Creates a char array from a TariWalletAddress in emoji format +/// +/// ## Arguments +/// `address` - The pointer to a TariWalletAddress +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut c_char` - Returns a pointer to a char array. Note that it returns empty +/// if emoji is null or if there was an error creating the emoji string from TariWalletAddress +/// +/// # Safety +/// The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak +#[no_mangle] +pub unsafe extern "C" fn byte_to_emoji(byte: u8) -> *mut c_char { + let emoji = EMOJI[byte as usize].to_string(); + let result = CString::new(emoji).expect("Emoji will not fail."); + CString::into_raw(result) +} + /// -------------------------------------------------------------------------------------------- /// /// /// ------------------------------- ComAndPubSignature Signature ---------------------------------------/// @@ -9144,7 +9195,7 @@ mod test { transaction_service::handle::TransactionSendStatus, }; use once_cell::sync::Lazy; - use tari_common_types::emoji; + use tari_common_types::{emoji, tari_address::TariAddressFeatures, types::PrivateKey}; use tari_comms::peer_manager::PeerFeatures; use tari_contacts::contacts_service::types::{Direction, Message, MessageMetadata}; use tari_core::{ @@ -9443,6 +9494,62 @@ mod test { } } + #[test] + fn test_emoji_convert() { + unsafe { + let byte = 0 as u8; + let emoji_ptr = byte_to_emoji(byte); + let emoji = CStr::from_ptr(emoji_ptr); + + assert_eq!(emoji.to_str().unwrap(), EMOJI[0].to_string()); + + let byte = 50 as u8; + let emoji_ptr = byte_to_emoji(byte); + let emoji = CStr::from_ptr(emoji_ptr); + + assert_eq!(emoji.to_str().unwrap(), EMOJI[50].to_string()); + + let byte = 125 as u8; + let emoji_ptr = byte_to_emoji(byte); + let emoji = CStr::from_ptr(emoji_ptr); + + assert_eq!(emoji.to_str().unwrap(), EMOJI[125].to_string()); + } + } + + #[test] + fn test_address_getters() { + unsafe { + let mut rng = rand::thread_rng(); + let view_key = PublicKey::from_secret_key(&PrivateKey::random(&mut rng)); + let spend_key = PublicKey::from_secret_key(&PrivateKey::random(&mut rng)); + + let address = TariAddress::new_dual_address( + view_key.clone(), + spend_key.clone(), + Network::Esmeralda, + TariAddressFeatures::create_one_sided_only(), + ); + let test_address = Box::into_raw(Box::new(address.clone())); + + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let ffi_features = tari_address_features_u8(test_address, error_ptr); + assert_eq!(address.features().as_u8(), ffi_features); + assert_eq!(*error_ptr, 0, "No error expected"); + + let ffi_checksum = tari_address_checksum_u8(test_address, error_ptr); + assert_eq!(address.calculate_checksum(), ffi_checksum); + assert_eq!(*error_ptr, 0, "No error expected"); + + let ffi_network = tari_address_network_u8(test_address, error_ptr); + assert_eq!(address.network() as u8, ffi_network); + assert_eq!(*error_ptr, 0, "No error expected"); + + tari_address_destroy(test_address); + } + } + #[test] fn test_emoji_set() { unsafe { diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index ce9aa84e20..e72bfa2ff2 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -831,6 +831,23 @@ uint8_t tari_address_checksum_u8(TariWalletAddress *address, char *tari_address_features(TariWalletAddress *address, int *error_out); +/** + * Creates a char array from a TariWalletAddress's features + * + * ## Arguments + * `address` - The pointer to a TariWalletAddress + * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions + * as an out parameter. + * + * ## Returns + * u8` - Returns u8 representing the features. On failure, returns 0. This may be valid so always check the error out + * + * # Safety + * The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak + */ +uint8_t tari_address_features_u8(TariWalletAddress *address, + int *error_out); + /** * Creates a public key from a TariWalletAddress's view key * @@ -882,6 +899,23 @@ TariPublicKey *tari_address_spend_key(TariWalletAddress *address, TariWalletAddress *emoji_id_to_tari_address(const char *emoji, int *error_out); +/** + * Creates a char array from a TariWalletAddress in emoji format + * + * ## Arguments + * `address` - The pointer to a TariWalletAddress + * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions + * as an out parameter. + * + * ## Returns + * `*mut c_char` - Returns a pointer to a char array. Note that it returns empty + * if emoji is null or if there was an error creating the emoji string from TariWalletAddress + * + * # Safety + * The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak + */ +char *byte_to_emoji(uint8_t byte); + /** * -------------------------------------------------------------------------------------------- /// * From 6fb5e69baedbef518f3936d2c15c6e03c5082bc1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Mon, 8 Jul 2024 07:45:03 +0200 Subject: [PATCH 2/6] fix doc --- base_layer/wallet_ffi/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 35c6424c1e..8baab5fbdd 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -1477,12 +1477,10 @@ pub unsafe extern "C" fn emoji_id_to_tari_address( } } -/// Creates a char array from a TariWalletAddress in emoji format +/// Does a lookup of the emoji character for a byte, using the emoji encoding of tari /// /// ## Arguments -/// `address` - The pointer to a TariWalletAddress -/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions -/// as an out parameter. +/// `byte` - u8 byte to lookup the emoji for /// /// ## Returns /// `*mut c_char` - Returns a pointer to a char array. Note that it returns empty From 25eba30d8ca6a089db0dbfe5624ec0e17cd2eaeb Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Mon, 8 Jul 2024 08:55:00 +0200 Subject: [PATCH 3/6] fix docs in .h file --- base_layer/wallet_ffi/wallet.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index e72bfa2ff2..4893aef241 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -900,12 +900,10 @@ TariWalletAddress *emoji_id_to_tari_address(const char *emoji, int *error_out); /** - * Creates a char array from a TariWalletAddress in emoji format + * Does a lookup of the emoji character for a byte, using the emoji encoding of tari * * ## Arguments - * `address` - The pointer to a TariWalletAddress - * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions - * as an out parameter. + * `byte` - u8 byte to lookup the emoji for * * ## Returns * `*mut c_char` - Returns a pointer to a char array. Note that it returns empty From c330cc2f1d5bae2cb4e46e48d6b7106c1d054edb Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Tue, 9 Jul 2024 11:45:34 +0200 Subject: [PATCH 4/6] WIP on sw_ffi_improvemtns --- base_layer/wallet_ffi/src/lib.rs | 43 +++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 8baab5fbdd..ff6dec2aa0 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -983,6 +983,35 @@ pub unsafe extern "C" fn public_key_get_bytes(pk: *mut TariPublicKey, error_out: Box::into_raw(Box::new(bytes)) } +/// Converts public key to emoji encding +/// +/// ## Arguments +/// `pk` - The pointer to a TariPublicKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut c_char` - Returns a pointer to a char array. Note that it returns empty +/// if emoji is null or if there was an error creating the emoji string from public key +/// +/// # Safety +/// The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak +#[no_mangle] +pub unsafe extern "C" fn public_key_get_emoji_encoding(pk: *mut TariPublicKey, error_out: *mut c_int) -> *mut c_char { + let mut error = 0; + let mut result = CString::new("").expect("Blank CString will not fail."); + ptr::swap(error_out, &mut error as *mut c_int); + if pk.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return CString::into_raw(result); + } + let pk_bytes = (*pk).to_vec(); + let emoji_string = pk_bytes.iter().map(|b| EMOJI[*b as usize]).collect::(); + result = CString::new(emoji_string).expect("Emoji will not fail."); + CString::into_raw(result) +} + /// Creates a TariPublicKey from a TariPrivateKey /// /// ## Arguments @@ -1379,7 +1408,7 @@ pub unsafe extern "C" fn tari_address_features_u8(address: *mut TariWalletAddres /// as an out parameter. /// /// ## Returns -/// `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns null +/// `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns null if there is no key present /// /// # Safety /// The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak @@ -1399,9 +1428,7 @@ pub unsafe extern "C" fn tari_address_view_key( match view_key { Some(key) => Box::into_raw(Box::new(key.clone())), None => { - error!(target: LOG_TARGET, "Error reading view key from Tari Address"); - error = 1; - ptr::swap(error_out, &mut error as *mut c_int); + debug!(target: LOG_TARGET, "No view key present on Tari Address"); ptr::null_mut() }, } @@ -9741,6 +9768,14 @@ mod test { assert_eq!(error, 0); let public_key_length = byte_vector_get_length(public_bytes, error_ptr); assert_eq!(error, 0); + let public_key_emoji = public_key_get_emoji_encoding(public_key, error_ptr); + assert_eq!(error, 0); + let emoji = CStr::from_ptr(public_key_emoji); + let rust_string = emoji.to_str().unwrap().to_string(); + let chars = rust_string.chars().collect::>(); + + assert_eq!(chars.len(), 32); + assert_eq!(private_key_length, 32); assert_eq!(public_key_length, 32); assert_ne!((*private_bytes), (*public_bytes)); From 0534e88154b9abf39e7677cdbe7b935a70961dd1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Tue, 9 Jul 2024 13:37:06 +0200 Subject: [PATCH 5/6] .h file --- base_layer/wallet_ffi/wallet.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index 4893aef241..321b19a998 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -639,6 +639,24 @@ void public_keys_destroy(struct TariPublicKeys *pks); struct ByteVector *public_key_get_bytes(TariPublicKey *pk, int *error_out); +/** + * Converts public key to emoji encding + * + * ## Arguments + * `pk` - The pointer to a TariPublicKey + * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions + * as an out parameter. + * + * ## Returns + * `*mut c_char` - Returns a pointer to a char array. Note that it returns empty + * if emoji is null or if there was an error creating the emoji string from public key + * + * # Safety + * The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak + */ +char *public_key_get_emoji_encoding(TariPublicKey *pk, + int *error_out); + /** * Creates a TariPublicKey from a TariPrivateKey * @@ -857,7 +875,7 @@ uint8_t tari_address_features_u8(TariWalletAddress *address, * as an out parameter. * * ## Returns - * `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns null + * `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns null if there is no key present * * # Safety * The ```string_destroy``` method must be called when finished with a string from rust to prevent a memory leak From 5ff6a9d64faeffd0405a96a1bacf2186a9ccf555 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 10 Jul 2024 11:50:40 +0200 Subject: [PATCH 6/6] Update base_layer/wallet_ffi/src/lib.rs Co-authored-by: Brian Pearce --- base_layer/wallet_ffi/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index ff6dec2aa0..d5eb33ceb5 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -983,7 +983,7 @@ pub unsafe extern "C" fn public_key_get_bytes(pk: *mut TariPublicKey, error_out: Box::into_raw(Box::new(bytes)) } -/// Converts public key to emoji encding +/// Converts public key to emoji encoding /// /// ## Arguments /// `pk` - The pointer to a TariPublicKey