Skip to content

Commit

Permalink
switch to rust for strings to avoid conversion and add freeing of str…
Browse files Browse the repository at this point in the history
…ings, mnemonic, and privateKey
  • Loading branch information
thomasthachil committed Aug 25, 2023
1 parent 763a118 commit cfdc36a
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 119 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
36 changes: 7 additions & 29 deletions ethers-ffi/src/android/EthersRs.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
package com.uniswap

/**
* This object is not used directly in this repo, but is included to show what the JNI bindings correspond with.
/**
* These functions are defined from an object to be used from a static context.
* The Rust implementation contains JNI bindings that are generated from the definition here.
*/
object EthersRs {
/**
* Generates a mnemonic and its associated address.
* @return A CMnemonicAndAddress object containing the generated mnemonic and its associated address.
*/
external fun generateMnemonic(): CMnemonicAndAddress


/**
* Frees the memory allocated for the mnemonic.
* @param mnemonic The mnemonic to be freed.
*/
external fun mnemonicFree(mnemonic: CMnemonicAndAddress)
external fun generateMnemonic(): MnemonicAndAddress

/**
* Generates a private key from a given mnemonic.
* @param mnemonic The mnemonic to generate the private key from.
* @param index The index of the private key to generate.
* @return A CPrivateKey object containing the generated private key.
*/
external fun privateKeyFromMnemonic(mnemonic: String?, index: Int): CPrivateKey

/**
* Frees the memory allocated for the private key.
* @param privateKey The private key to be freed.
*/
external fun privateKeyFree(privateKey: CPrivateKey)
external fun privateKeyFromMnemonic(mnemonic: String?, index: Int): PrivateKeyAndAddress

/**
* Creates a wallet from a given private key.
Expand Down Expand Up @@ -80,34 +68,24 @@ object EthersRs {
hash: String,
chainId: Long
): String

/**
* Frees the memory allocated for the string.
* @param string The string to be freed.
*/
external fun stringFree(string: String)
}

/**
* Represents a private key and its associated address.
* @property privateKey The private key.
* @property address The address associated with the private key.
* @property handle This is a pointer to a Rust CPrivateKey struct.
*/
class CPrivateKey(
class PrivateKeyAndAddress(
var privateKey: String,
var address: String,
var handle: Long
)

/**
* Represents a mnemonic and its associated address.
* @property mnemonic The mnemonic phrase.
* @property address The address associated with the mnemonic.
* @property handle This is a pointer to a Rust CMnemonicAndAddress struct.
*/
class CMnemonicAndAddress(
class MnemonicAndAddress(
var mnemonic: String,
var address: String,
var handle: Long
)
129 changes: 39 additions & 90 deletions ethers-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,20 @@ pub struct SignedTransaction {
pub struct CSignedTransaction {
signature: *const c_char,
}

#[no_mangle]
pub extern "C" fn generate_mnemonic() -> CMnemonicAndAddress {
fn generate_mnemonic_rust() -> MnemonicAndAddress {
let rng = &mut rand::thread_rng();
let mnemonic = Mnemonic::<English>::new_with_count(rng, 12).unwrap().to_phrase().unwrap();
let mnem_clone = mnemonic.clone();
let private_key = derive_private_key(mnemonic, 0);

let mnemonic_struct = MnemonicAndAddress { mnemonic: mnem_clone, address: private_key.address };
return mnemonic_struct
}

#[no_mangle]
pub extern "C" fn generate_mnemonic() -> CMnemonicAndAddress {
let mnemonic_struct = generate_mnemonic_rust();
return CMnemonicAndAddress::c_repr_of(mnemonic_struct).unwrap()
}

Expand All @@ -106,6 +110,12 @@ fn derive_private_key(mnemonic_str: String, index: u32) -> PrivateKey {
return PrivateKey { private_key: private_key_str, address }
}

#[no_mangle]
fn private_key_from_mnemonic_rust(mnemonic: String, index: u32) -> PrivateKey {
let priv_struct = derive_private_key(mnemonic, index);
return priv_struct
}

#[no_mangle]
pub extern "C" fn private_key_from_mnemonic(mnemonic: *const c_char, index: u32) -> CPrivateKey {
let mnemonic_c_str = unsafe {
Expand Down Expand Up @@ -255,6 +265,11 @@ pub mod android {
env.new_string(rust_string).expect("Failed to create Java string")
}

fn jstring_to_rust_string(env: &JNIEnv, jstring: JString) -> String {
let jstr = env.get_string(jstring).expect("Couldn't get java string!");
return jstr.to_str().expect("Invalid UTF-8 in java string").to_string();
}

fn jstring_to_cstring(env: &JNIEnv, jstring: JString) -> CString {
let jstr = env.get_string(jstring).expect("Couldn't get java string!");
let cstring = CString::new(jstr.to_str().expect("Invalid UTF-8 in java string"))
Expand All @@ -264,122 +279,61 @@ pub mod android {

#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_generateMnemonic(env: JNIEnv, _class: JClass) -> jobject{
let mnemonic_struct = generate_mnemonic();
let mnemonic_struct = generate_mnemonic_rust();

let class_name = "com/uniswap/CMnemonicAndAddress";
let class_name = "com/uniswap/MnemonicAndAddress";
let class = env
.find_class(class_name)
.expect(&format!("Failed to find class: {}", class_name));

// Convert the mnemonic and address fields to Rust String
let mnemonic = unsafe { CStr::from_ptr(mnemonic_struct.mnemonic).to_string_lossy().into_owned() };
let address = unsafe { CStr::from_ptr(mnemonic_struct.address).to_string_lossy().into_owned() };

// Create a new Java string from the Rust string
let mnemonic_jstring = rust_string_to_jstring(&env, mnemonic);
let address_jstring = rust_string_to_jstring(&env, address);

// Create a Box that owns the mnemonic_struct (box) is a smart pointer that allocates data on the heap).
// When this Box is dropped (goes out of scope), it will deallocate the mnemonic_struct as well.
let mnemonic_box = Box::new(mnemonic_struct);
let mnemonic_ptr = Box::into_raw(mnemonic_box);

// Cast the raw pointer to a jlong, which is a pointer to a JLong object
let handle = mnemonic_ptr as jlong;
let mnemonic_jstring = rust_string_to_jstring(&env, mnemonic_struct.mnemonic.clone());
let address_jstring = rust_string_to_jstring(&env, mnemonic_struct.address.clone());

let object = env
.new_object(
class,
"(Ljava/lang/String;Ljava/lang/String;J)V",
"(Ljava/lang/String;Ljava/lang/String;)V",
&[
JValue::Object(JObject::from(mnemonic_jstring).into()),
JValue::Object(JObject::from(address_jstring).into()),
JValue::Long(handle)
JValue::Object(JObject::from(address_jstring).into())
],
)
.expect("Failed to create CMnemonicAndAddress object");
.expect("Failed to create MnemonicAndAddress object");

//consume the JObject and return the underlying jobject, which is a raw pointer to the Java object.
object.into_inner()
}

#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_privateKeyFromMnemonic(env: JNIEnv, _class: JClass, mnemonic: JString, index: jlong) -> jobject {
let mnemonic_cstring = jstring_to_cstring(&env, mnemonic);
let mnemonic_ptr = mnemonic_cstring.as_ptr() as *const c_char;

let private_key_struct = private_key_from_mnemonic(mnemonic_ptr, index as u32);

let mnemonic_string = jstring_to_rust_string(&env, mnemonic);
let private_key_struct = private_key_from_mnemonic_rust(mnemonic_string, index as u32);

let class_name = "com/uniswap/CPrivateKey";
let class_name = "com/uniswap/PrivateKeyAndAddress";
let class = env
.find_class(class_name)
.expect(&format!("Failed to find class: {}", class_name));

// Convert the mnemonic and address fields to Rust String
let private_key = unsafe { CStr::from_ptr(private_key_struct.private_key).to_string_lossy().into_owned() };
let address = unsafe { CStr::from_ptr(private_key_struct.address).to_string_lossy().into_owned() };

// Create a new Java string from the Rust string
let private_key_jstring = rust_string_to_jstring(&env, private_key);
let address_jstring = rust_string_to_jstring(&env, address);

// Create a Box that owns the prv struct (box) is a smart pointer that allocates data on the heap).
// When this Box is dropped (goes out of scope), it will deallocate prv as well.
let private_key_box = Box::new(private_key_struct);
let private_key_ptr = Box::into_raw(private_key_box);

// Cast the raw pointer to a jlong, which is a pointer to a JLong object
let handle = private_key_ptr as jlong;
let private_key_jstring = rust_string_to_jstring(&env, private_key_struct.private_key.clone());
let address_jstring = rust_string_to_jstring(&env, private_key_struct.address.clone());

// Create a new instance of CMnemonicAndAddress
let object = env
.new_object(
class,
"(Ljava/lang/String;Ljava/lang/String;J)V",
"(Ljava/lang/String;Ljava/lang/String;)V",
&[
JValue::Object(JObject::from(private_key_jstring).into()),
JValue::Object(JObject::from(address_jstring).into()),
JValue::Long(handle)
],
)
.expect("Failed to create CPrivateKey object");
.expect("Failed to create PrivateKey object");
object.into_inner()
}

// Function to free the mnemonic
#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_mnemonicFree(env: JNIEnv, _class: JClass, mnemonic: jobject) {
// Get the handle field (which is a pointer to the Rust object) from the Java object
let mnemonic_object = JObject::from(mnemonic);
let handle = env.get_field(mnemonic_object, "handle", "J").expect("Should be able to get field handle").j().expect("handle should be a long");

// Cast the handle to a pointer to the Rust object
let mnemonic_ptr = handle as *mut CMnemonicAndAddress;

// Take ownership of the Rust object and free it
let mnemonic_struct = unsafe { opaque_pointer::own_back(mnemonic_ptr).unwrap() };
mnemonic_free(mnemonic_struct);
}

// Function to free the private key
#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_privateKeyFree(env: JNIEnv, _class: JClass, private_key: jobject) {
// Convert the jobject to a JObject
let private_key_object = JObject::from(private_key);

// Get the handle field from the JObject. The handle is a jlong that we will cast to a pointer.
let handle = env.get_field(private_key_object, "handle", "J")
.expect("Should be able to get field handle")
.j().expect("handle should be a long");

// Cast the handle to a pointer to the Rust object
let private_key_ptr = handle as *mut CPrivateKey;

// Take ownership of the Rust object and free it
let private_key_struct = unsafe { opaque_pointer::own_back(private_key_ptr).unwrap() };
private_key_free(private_key_struct);
}

// Function to get a wallet from a private key
#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_walletFromPrivateKey(env: JNIEnv, _class: JClass, private_key: JString) -> u64 {
Expand All @@ -388,7 +342,7 @@ pub mod android {
let wallet_ptr_long: u64 = wallet_ptr as u64;
wallet_ptr_long
}

// Function to free the wallet
#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_walletFree(_env: JNIEnv, _class: JClass, wallet_ptr: jlong) {
Expand Down Expand Up @@ -427,6 +381,9 @@ pub mod android {
// Convert the Rust String to a JString
let output = env.new_string(signature).expect("Couldn't create java string!");

// Free signature pointer in C
string_free(signature_ptr);

// Return the JString
output.into_inner()
}
Expand All @@ -448,18 +405,10 @@ pub mod android {
// Convert the Rust String to a JString
let output = env.new_string(signature).expect("Couldn't create java string!");

// Free signature pointer in C
string_free(signature_ptr);

// Return the JString
output.into_inner()
}

// Function to free a string
#[no_mangle]
pub extern "system" fn Java_com_uniswap_EthersRs_stringFree(env: JNIEnv, _class: JClass, string: JString) {

let c_string_str = jstring_to_cstring(&env, string);
let c_string_ptr = c_string_str.into_raw(); // makes mutable raw pointer

// Free the string
string_free(c_string_ptr);
}
}

0 comments on commit cfdc36a

Please sign in to comment.