diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index aaf428fd2d7..03fb040a886 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -32,28 +32,36 @@ pub struct TWArg { pub ty: String, } +fn convert_standard_type_to_cpp(ty: &str) -> String { + match ty { + "TWPrivateKey" => "struct TWPrivateKey".to_string(), + "TWPublicKey" => "struct TWPublicKey".to_string(), + _ => ty.to_string(), + } +} + fn convert_rust_type_to_cpp(ty: &str) -> String { let trimmed = ty.replace(" ", ""); if let Some(captures) = Regex::new(r"^Nonnull<(.+)>$") .expect("Failed to create regex") .captures(&trimmed) { - format!("{} *_Nonnull", &captures[1]) + format!("{} *_Nonnull", convert_standard_type_to_cpp(&captures[1])) } else if let Some(captures) = Regex::new(r"^NonnullMut<(.+)>$") .expect("Failed to create regex") .captures(&trimmed) { - format!("{} *_Nonnull", &captures[1]) + format!("{} *_Nonnull", convert_standard_type_to_cpp(&captures[1])) } else if let Some(captures) = Regex::new(r"^Nullable<(.+)>$") .expect("Failed to create regex") .captures(&trimmed) { - format!("{} *_Nullable", &captures[1]) + format!("{} *_Nullable", convert_standard_type_to_cpp(&captures[1])) } else if let Some(captures) = Regex::new(r"^NullableMut<(.+)>$") .expect("Failed to create regex") .captures(&trimmed) { - format!("{} *_Nullable", &captures[1]) + format!("{} *_Nullable", convert_standard_type_to_cpp(&captures[1])) } else { match ty { "u8" => "uint8_t".to_string(), @@ -91,6 +99,15 @@ fn generate_header_includes(file: &mut std::fs::File, info: &TWConfig) -> Result if arg.ty.contains("TWString") && included_headers.insert("TWString.h") { writeln!(file, "#include \"TWString.h\"")?; } + if arg.ty.contains("TWData") && included_headers.insert("TWData.h") { + writeln!(file, "#include \"TWData.h\"")?; + } + if arg.ty.contains("TWPrivateKey") && included_headers.insert("TWPrivateKey.h") { + writeln!(file, "#include \"TWPrivateKey.h\"")?; + } + if arg.ty.contains("TWPublicKey") && included_headers.insert("TWPublicKey.h") { + writeln!(file, "#include \"TWPublicKey.h\"")?; + } // Additional type checks can be added here in the future } } @@ -168,12 +185,23 @@ pub fn generate_header(info: &TWConfig) -> Result<()> { } fn generate_source_includes(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { - writeln!( - file, - "#include ", - info.class - )?; + writeln!(file, "#include ", info.class)?; writeln!(file, "#include \"rust/Wrapper.h\"")?; + + // Include headers based on argument types + let mut included_headers = std::collections::HashSet::new(); + for func in &info.static_functions { + for arg in &func.args { + if arg.ty.contains("TWPrivateKey") && included_headers.insert("TWPrivateKey.h") { + writeln!(file, "#include \"../PrivateKey.h\"")?; + } + if arg.ty.contains("TWPublicKey") && included_headers.insert("TWPublicKey.h") { + writeln!(file, "#include \"../PublicKey.h\"")?; + } + // Additional type checks can be added here in the future + } + } + Ok(()) } @@ -185,7 +213,7 @@ fn generate_function_call(args: &Vec) -> Result { func_call += ", "; } } - func_call += ");\n"; + func_call += ");"; Ok(func_call) } @@ -195,23 +223,66 @@ fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) - "NullableMut" | "Nullable" => { write!( &mut return_string, - " const Rust::TWStringWrapper result = Rust::{}", - func.rust_name + "\tconst Rust::TWStringWrapper result = Rust::{}{}\n\ + \tif (!result) {{ return nullptr; }}\n\ + \treturn TWStringCreateWithUTF8Bytes(result.c_str());\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() ) .map_err(|e| BadFormat(e.to_string()))?; - return_string += generate_function_call(&converted_args)?.as_str(); - writeln!(&mut return_string, " if (!result) {{ return nullptr; }}") - .map_err(|e| BadFormat(e.to_string()))?; - writeln!( + } + "NullableMut" | "Nullable" => { + write!( + &mut return_string, + "\tconst Rust::TWDataWrapper result = Rust::{}{}\n\ + \tif (!result.ptr) {{ return nullptr; }}\n\ + \tconst auto resultData = result.toDataOrDefault();\n\ + \treturn TWDataCreateWithBytes(resultData.data(), resultData.size());\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() + ) + .map_err(|e| BadFormat(e.to_string()))?; + } + "NullableMut" | "Nullable" => { + write!( + &mut return_string, + "\tconst auto result = Rust::{}{}\n\ + \tconst auto resultRustPrivateKey = Rust::wrapTWPrivateKey(result);\n\ + \tif (!resultRustPrivateKey.get()) {{ return nullptr; }}\n\ + \tconst auto resultData = Rust::tw_private_key_bytes(resultRustPrivateKey.get());\n\ + \tconst auto resultSize = Rust::tw_private_key_size(resultRustPrivateKey.get());\n\ + \tconst Data out(resultData, resultData + resultSize);\n\ + \treturn new TWPrivateKey {{ PrivateKey(out) }};\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() + ) + .map_err(|e| BadFormat(e.to_string()))?; + } + "NullableMut" | "Nullable" => { + write!( &mut return_string, - " return TWStringCreateWithUTF8Bytes(result.c_str());" + "\tconst auto result = Rust::{}{}\n\ + \tconst auto resultRustPublicKey = Rust::wrapTWPublicKey(result);\n\ + \tif (!resultRustPublicKey.get()) {{ return nullptr; }}\n\ + \tconst auto resultData = Rust::tw_public_key_data(resultRustPublicKey.get());\n\ + \tconst Data out(resultData.data, resultData.data + resultData.size);\n\ + \treturn new TWPublicKey {{ PublicKey(out, a->impl.type) }};\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() ) .map_err(|e| BadFormat(e.to_string()))?; } + ty if ty.contains("Nonnull") => { + panic!("Nonnull types are not supported in C++"); + } _ => { - write!(&mut return_string, " return Rust::{}", func.rust_name) - .map_err(|e| BadFormat(e.to_string()))?; - return_string += generate_function_call(&converted_args)?.as_str(); + write!( + &mut return_string, + "\treturn Rust::{}{}\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() + ) + .map_err(|e| BadFormat(e.to_string()))?; } } Ok(return_string) @@ -233,17 +304,85 @@ fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> Result<(Strin let mut conversion_code = String::new(); writeln!( &mut conversion_code, - "\tconst TW::Rust::TWString* {name}Ptr;\n\ + "\tRust::TWStringWrapper {name}RustStr;\n\ + \tif ({name} != nullptr) {{\n\ + \t\t{name}RustStr = *reinterpret_cast({name});\n\ + \t}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustStr.get()", name))) + } + "TWData *_Nonnull" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto& {name}Data = *reinterpret_cast({name});\n\ + \tconst Rust::TWDataWrapper {name}RustData = {name}Data;" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustData.get()", name))) + } + "TWData *_Nullable" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tRust::TWDataWrapper {name}RustData;\n\ + \tif ({name} != nullptr) {{\n\ + \t\t{name}RustData = *reinterpret_cast({name});\n\ + \t}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustData.get()", name))) + } + "struct TWPrivateKey *_Nonnull" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto &{name}PrivateKey = *reinterpret_cast(a);\n\ + \tauto* {name}RustRaw = Rust::tw_private_key_create_with_data({name}PrivateKey.bytes.data(), {name}PrivateKey.bytes.size());\n\ + \tconst auto {name}RustPrivateKey = Rust::wrapTWPrivateKey({name}RustRaw);" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPrivateKey.get()", name))) + } + "struct TWPrivateKey *_Nullable" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tstd::shared_ptr {name}RustPrivateKey;\n\ + \tif ({name} != nullptr) {{\n\ + \t\tconst auto& {name}PrivateKey = {name};\n\ + \t\tauto* {name}RustRaw = Rust::tw_private_key_create_with_data({name}PrivateKey->impl.bytes.data(), {name}PrivateKey->impl.bytes.size());\n\ + \t\t{name}RustPrivateKey = Rust::wrapTWPrivateKey({name}RustRaw);\n\ + \t}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPrivateKey.get()", name))) + } + "struct TWPublicKey *_Nonnull" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto &{name}PublicKey = *reinterpret_cast(a);\n\ + \tauto* {name}RustRaw = Rust::tw_public_key_create_with_data({name}PublicKey.bytes.data(), {name}PublicKey.bytes.size(), {name}PublicKey.type);\n\ + \tconst auto {name}RustPublicKey = Rust::wrapTWPublicKey({name}RustRaw);" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPublicKey.get()", name))) + } + "struct TWPublicKey *_Nullable" => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tstd::shared_ptr {name}RustPublicKey;\n\ \tif ({name} != nullptr) {{\n\ - \t\tauto& {name}String = *reinterpret_cast({name});\n\ - \t\tconst Rust::TWStringWrapper {name}RustStr = {name}String;\n\ - \t\t{name}Ptr = {name}RustStr.get();\n\ - \t}} else {{\n\ - \t\t{name}Ptr = nullptr;\n\ + \t\tconst auto& {name}PublicKey = {name};\n\ + \t\tauto* {name}RustRaw = Rust::tw_public_key_create_with_data({name}PublicKey->impl.bytes.data(), {name}PublicKey->impl.bytes.size(), {name}PublicKey->impl.type);\n\ + \t\t{name}RustPublicKey = Rust::wrapTWPublicKey({name}RustRaw);\n\ \t}}" ) .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}Ptr", name))) + Ok((conversion_code, format!("{}RustPublicKey.get()", name))) } _ => Ok(("".to_string(), name.to_string())), } diff --git a/rust/tw_keypair/src/ffi/privkey.rs b/rust/tw_keypair/src/ffi/privkey.rs index 9d4efa7f8f4..49436e1a2cb 100644 --- a/rust/tw_keypair/src/ffi/privkey.rs +++ b/rust/tw_keypair/src/ffi/privkey.rs @@ -50,6 +50,28 @@ pub unsafe extern "C" fn tw_private_key_delete(key: *mut TWPrivateKey) { let _ = TWPrivateKey::from_ptr(key); } +/// Returns the raw pointer to the underlying bytes of the private key. +/// +/// \param data A non-null valid block of private key +/// \return the raw pointer to the contents of private key +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_bytes(data: *const TWPrivateKey) -> *const u8 { + TWPrivateKey::from_ptr_as_ref(data) + .map(|data| data.0.bytes().as_ptr()) + .unwrap_or_else(std::ptr::null) +} + +/// Returns the size in bytes. +/// +/// \param data A non-null valid block of private key +/// \return the size of the given block of private key +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_size(data: *const TWPrivateKey) -> usize { + TWPrivateKey::from_ptr_as_ref(data) + .map(|data| data.0.bytes().len()) + .unwrap_or_default() +} + /// Determines if the given private key is valid or not. /// /// \param key *non-null* byte array. diff --git a/rust/tw_keypair/src/tw/private.rs b/rust/tw_keypair/src/tw/private.rs index 0516f451798..08cf8c18e80 100644 --- a/rust/tw_keypair/src/tw/private.rs +++ b/rust/tw_keypair/src/tw/private.rs @@ -37,6 +37,10 @@ impl PrivateKey { Ok(PrivateKey { bytes }) } + pub fn bytes(&self) -> &[u8] { + &self.bytes + } + /// Returns the 32 byte array - the essential private key data. pub fn key(&self) -> H256 { assert!( diff --git a/rust/wallet_core_rs/src/ffi/ffi_test.rs b/rust/wallet_core_rs/src/ffi/ffi_test.rs index c8e7739b7ae..3653a988f36 100644 --- a/rust/wallet_core_rs/src/ffi/ffi_test.rs +++ b/rust/wallet_core_rs/src/ffi/ffi_test.rs @@ -4,8 +4,14 @@ #![allow(clippy::missing_safety_doc)] +use tw_keypair::ffi::{ + privkey::{tw_private_key_create_with_data, TWPrivateKey}, + pubkey::{tw_public_key_create_with_data, TWPublicKey}, +}; use tw_macros::tw_ffi; -use tw_memory::ffi::{tw_string::TWString, Nonnull, NullableMut, RawPtrTrait}; +use tw_memory::ffi::{ + tw_data::TWData, tw_string::TWString, Nonnull, Nullable, NullableMut, RawPtrTrait, +}; use tw_misc::try_or_else; /// Sum two unsigned integers of 8 bits @@ -68,7 +74,7 @@ pub unsafe extern "C" fn tw_ffi_test_signed_sum_i64(a: i64, b: i64) -> i64 { #[tw_ffi(ty = static_function, class = TWFFITest, name = StringWithU8)] #[no_mangle] pub unsafe extern "C" fn tw_ffi_test_string_with_u8( - a: Nonnull, + a: Nullable, b: u8, ) -> NullableMut { let a = try_or_else!(TWString::from_ptr_as_ref(a), std::ptr::null_mut); @@ -78,3 +84,73 @@ pub unsafe extern "C" fn tw_ffi_test_string_with_u8( result.push(b as char); TWString::from(result).into_ptr() } + +#[tw_ffi(ty = static_function, class = TWFFITest, name = DataWithU8)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_data_with_u8( + a: Nonnull, + b: u8, +) -> NullableMut { + let a = try_or_else!(TWData::from_ptr_as_ref(a), std::ptr::null_mut); + let mut data = Vec::new(); + data.extend_from_slice(a.clone().into_vec().as_slice()); + data.push(b); + TWData::from(data).into_ptr() +} + +#[tw_ffi(ty = static_function, class = TWFFITest, name = NullableDataWithU8)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_nullable_data_with_u8( + a: Nullable, + b: u8, +) -> NullableMut { + let a = try_or_else!(TWData::from_ptr_as_ref(a), std::ptr::null_mut); + let mut data = Vec::new(); + data.extend_from_slice(a.clone().into_vec().as_slice()); + data.push(b); + TWData::from(data).into_ptr() +} + +#[tw_ffi(ty = static_function, class = TWFFITest, name = PrivateKey)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_private_key( + a: Nonnull, +) -> NullableMut { + let c = try_or_else!(TWPrivateKey::from_ptr_as_ref(a), std::ptr::null_mut); + tw_private_key_create_with_data(c.as_ref().bytes().as_ptr(), c.as_ref().bytes().len()) +} + +#[tw_ffi(ty = static_function, class = TWFFITest, name = NullablePrivateKey)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_nullable_private_key( + a: Nullable, +) -> NullableMut { + let c = try_or_else!(TWPrivateKey::from_ptr_as_ref(a), std::ptr::null_mut); + tw_private_key_create_with_data(c.as_ref().bytes().as_ptr(), c.as_ref().bytes().len()) +} + +#[tw_ffi(ty = static_function, class = TWFFITest, name = PublicKey)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_public_key( + a: Nonnull, +) -> NullableMut { + let c = try_or_else!(TWPublicKey::from_ptr_as_ref(a), std::ptr::null_mut); + tw_public_key_create_with_data( + c.as_ref().to_bytes().as_ptr(), + c.as_ref().to_bytes().len(), + c.as_ref().public_key_type() as u32, + ) +} + +#[tw_ffi(ty = static_function, class = TWFFITest, name = NullablePublicKey)] +#[no_mangle] +pub unsafe extern "C" fn tw_ffi_test_nullable_public_key( + a: Nullable, +) -> NullableMut { + let c = try_or_else!(TWPublicKey::from_ptr_as_ref(a), std::ptr::null_mut); + tw_public_key_create_with_data( + c.as_ref().to_bytes().as_ptr(), + c.as_ref().to_bytes().len(), + c.as_ref().public_key_type() as u32, + ) +} diff --git a/src/rust/Wrapper.h b/src/rust/Wrapper.h index 04d97a9104c..14cb997ea6c 100644 --- a/src/rust/Wrapper.h +++ b/src/rust/Wrapper.h @@ -20,6 +20,10 @@ inline std::shared_ptr wrapTWPublicKey(TWPublicKey* publicKey) { return std::shared_ptr(publicKey, tw_public_key_delete); } +inline std::shared_ptr wrapTWPrivateKey(TWPrivateKey* privateKey) { + return std::shared_ptr(privateKey, tw_private_key_delete); +} + struct TWDataVectorWrapper { TWDataVectorWrapper(): ptr(std::shared_ptr(tw_data_vector_create(), Rust::tw_data_vector_delete)) { @@ -62,6 +66,8 @@ struct TWDataWrapper { TWDataWrapper(TWData *ptr): ptr(std::shared_ptr(ptr, tw_data_delete)) { } + TWDataWrapper() = default; + ~TWDataWrapper() = default; TWData* get() const { @@ -98,6 +104,8 @@ struct TWStringWrapper { ptr = std::shared_ptr(stringRaw, tw_string_delete); } + TWStringWrapper() = default; + ~TWStringWrapper() = default; TWString* get() const {