diff --git a/.gitignore b/.gitignore index 9c41a67c770..e7e97f12830 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ swift/Sources/Generated swift/wallet-core/ codegen-v2/bindings/ +src/Generated/*.h src/Generated/*.cpp include/TrustWalletCore/TWHRP.h include/TrustWalletCore/TW*Proto.h @@ -48,6 +49,8 @@ include/TrustWalletCore/TWTONMessageSigner.h include/TrustWalletCore/TWMessageSigner.h include/TrustWalletCore/TWWalletConnectRequest.h include/TrustWalletCore/TWSolanaTransaction.h +include/TrustWalletCore/TWCryptoBoxPublicKey.h +include/TrustWalletCore/TWCryptoBoxSecretKey.h # Wasm emsdk/ diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index 03ebc852c97..e83f3af603e 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -1,10 +1,9 @@ use heck::ToLowerCamelCase; -use regex::Regex; -use serde::{Deserialize, Serialize}; use std::fmt::Write as _; use std::fs; use std::io::Write; +use super::code_gen_types::*; use crate::Error::BadFormat; use crate::Result; @@ -12,74 +11,6 @@ static IN_DIR: &str = "../rust/bindings/"; static HEADER_OUT_DIR: &str = "../include/TrustWalletCore/"; static SOURCE_OUT_DIR: &str = "../src/Generated/"; -#[derive(Deserialize, Serialize, Debug)] -pub struct TWConfig { - pub class: String, - pub static_functions: Vec, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct TWStaticFunction { - pub name: String, - pub rust_name: String, - pub args: Vec, - pub return_type: String, - pub docs: Vec, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct TWArg { - pub name: String, - pub ty: String, -} - -fn convert_standard_type_to_cpp(ty: &str) -> String { - match ty { - "TWPrivateKey" => "struct TWPrivateKey".to_string(), - "TWPublicKey" => "struct TWPublicKey".to_string(), - "TWDataVector" => "struct TWDataVector".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", 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", 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", 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", convert_standard_type_to_cpp(&captures[1])) - } else { - match ty { - "u8" => "uint8_t".to_string(), - "u16" => "uint16_t".to_string(), - "u32" => "uint32_t".to_string(), - "u64" => "uint64_t".to_string(), - "i8" => "int8_t".to_string(), - "i16" => "int16_t".to_string(), - "i32" => "int32_t".to_string(), - "i64" => "int64_t".to_string(), - "TWFFICoinType" => "enum TWCoinType".to_string(), - _ => ty.to_string(), - } - } -} - fn generate_license(file: &mut std::fs::File) -> Result<()> { writeln!(file, "// SPDX-License-Identifier: Apache-2.0")?; writeln!(file, "//")?; @@ -97,27 +28,27 @@ fn generate_header_includes(file: &mut std::fs::File, info: &TWConfig) -> Result // 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("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\"")?; + for (_, func) in info.functions(true) { + for ty in func.types() { + let tw_type = TWType::from(ty); + match tw_type { + TWType::Pointer(_, header_name) => { + if header_name == info.class { + continue; + } + if included_headers.insert(header_name.clone()) { + writeln!(file, "#include \"{}.h\"", header_name)?; + } + } + TWType::Standard(ty) => { + if ty.contains("TWFFICoinType") + && included_headers.insert("TWCoinType.h".to_string()) + { + // Need to handle this case separately because it's not a pointer type + writeln!(file, "#include \"TWCoinType.h\"")?; + } + } } - if arg.ty.contains("TWDataVector") && included_headers.insert("TWDataVector.h") { - writeln!(file, "#include \"TWDataVector.h\"")?; - } - if arg.ty.contains("TWFFICoinType") && included_headers.insert("TWCoinType.h") { - writeln!(file, "#include \"TWCoinType.h\"")?; - } - // Additional type checks can be added here in the future } } @@ -131,12 +62,19 @@ fn generate_class_declaration(file: &mut std::fs::File, info: &TWConfig) -> Resu fn generate_function_signature( class_name: &str, - func: &TWStaticFunction, + func_type: TWFunctionType, + func: &TWFunction, is_declaration: bool, ) -> Result { - let return_type = convert_rust_type_to_cpp(&func.return_type); + let return_type = TWType::from(func.return_type.clone()).cpp_type(); let whether_export = if is_declaration { - "TW_EXPORT_STATIC_METHOD " + match func_type { + TWFunctionType::StaticFunction | TWFunctionType::Constructor => { + "TW_EXPORT_STATIC_METHOD " + } + TWFunctionType::Method | TWFunctionType::Destructor => "TW_EXPORT_METHOD ", + TWFunctionType::Property => "TW_EXPORT_PROPERTY ", + } } else { "" }; @@ -146,7 +84,7 @@ fn generate_function_signature( write!( &mut signature, "{} {}", - convert_rust_type_to_cpp(&arg.ty), + TWType::from(arg.ty.clone()).cpp_type(), arg.name.to_lower_camel_case() ) .map_err(|e| BadFormat(e.to_string()))?; @@ -161,9 +99,10 @@ fn generate_function_signature( fn generate_function_declaration( file: &mut std::fs::File, class_name: &str, - func: &TWStaticFunction, + func_type: TWFunctionType, + func: &TWFunction, ) -> Result<()> { - let func_dec = generate_function_signature(class_name, func, true)?; + let func_dec = generate_function_signature(class_name, func_type, func, true)?; for doc in &func.docs { writeln!(file, "/// {}", doc)?; } @@ -182,8 +121,8 @@ pub fn generate_header(info: &TWConfig) -> Result<()> { writeln!(file, "\nTW_EXTERN_C_BEGIN\n")?; generate_class_declaration(&mut file, info)?; - for func in &info.static_functions { - generate_function_declaration(&mut file, &info.class, func)?; + for (func_type, func) in info.functions(true) { + generate_function_declaration(&mut file, &info.class, func_type, func)?; } writeln!(file, "TW_EXTERN_C_END")?; @@ -193,24 +132,74 @@ pub fn generate_header(info: &TWConfig) -> Result<()> { Ok(()) } +fn generate_wrapper_header(info: &TWConfig) -> Result<()> { + let class_name = &info.class; + let wrapper_class_name = class_name.replace("TW", ""); + let file_path = format!("{SOURCE_OUT_DIR}/{}.h", wrapper_class_name); + let mut file = std::fs::File::create(&file_path)?; + + generate_license(&mut file)?; + generate_header_guard(&mut file)?; + + writeln!(file, "#include \"rust/Wrapper.h\"\n")?; + + writeln!( + file, + "using {wrapper_class_name}Ptr = std::shared_ptr;\n", + )?; + + writeln!(file, "struct {} {{", wrapper_class_name)?; + + let Some(destructor) = &info.destructor else { + panic!("No destructor found for {}", wrapper_class_name); + }; + let destructor_name = &destructor.rust_name; + writeln!( + file, + "\texplicit {wrapper_class_name}(TW::Rust::{class_name}* raw_ptr): ptr(raw_ptr, TW::Rust::{destructor_name}) {{}}\n", + )?; + + writeln!(file, "\t{wrapper_class_name}Ptr ptr;")?; + writeln!(file, "}};\n")?; + + writeln!(file, "struct {} {{", class_name)?; + writeln!(file, "\t{wrapper_class_name} impl;")?; + writeln!(file, "}};\n")?; + + Ok(()) +} + fn generate_source_includes(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { 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") { + for (_, func) in info.functions(true) { + for ty in func.types() { + let tw_type = TWType::from(ty); + let TWType::Pointer(_, header_name) = tw_type else { + continue; + }; + if header_name.contains("TWPrivateKey") + && included_headers.insert("TWPrivateKey.h".to_string()) + { writeln!(file, "#include \"../PrivateKey.h\"")?; - } - if arg.ty.contains("TWPublicKey") && included_headers.insert("TWPublicKey.h") { + } else if header_name.contains("TWPublicKey") + && included_headers.insert("TWPublicKey.h".to_string()) + { writeln!(file, "#include \"../PublicKey.h\"")?; - } - if arg.ty.contains("TWDataVector") && included_headers.insert("TWDataVector.h") { + } else if header_name.contains("TWDataVector") + && included_headers.insert("TWDataVector.h".to_string()) + { writeln!(file, "#include \"../DataVector.h\"")?; + } else if !header_name.contains("TWString") && !header_name.contains("TWData") { + // Do not need wrapper headers for these types + let wrapper_header_name = header_name.replace("TW", ""); + if included_headers.insert(wrapper_header_name.clone()) { + writeln!(file, "#include \"{}.h\"", wrapper_header_name)?; + } } - // Additional type checks can be added here in the future } } @@ -229,223 +218,273 @@ fn generate_function_call(args: &Vec) -> Result { Ok(func_call) } -fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) -> Result { +fn generate_return_type(func: &TWFunction, converted_args: &Vec) -> Result { let mut return_string = String::new(); - match func.return_type.replace(" ", "").as_str() { - "NullableMut" | "Nullable" => { - write!( - &mut return_string, - "\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()))?; - } - "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()))?; - } - "Nonnull" | "NonnullMut" => { - write!( - &mut return_string, - "\tconst Rust::TWDataWrapper result = Rust::{}{}\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, - "\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++ except for TWData"); - } - _ => { - write!( - &mut return_string, - "\treturn Rust::{}{}\n", - func.rust_name, - generate_function_call(&converted_args)?.as_str() - ) - .map_err(|e| BadFormat(e.to_string()))?; - } + let tw_type = TWType::from(func.return_type.replace(" ", "").to_string()); + match tw_type { + TWType::Pointer(pointer_type, ty) => match (pointer_type, ty.as_str()) { + (TWPointerType::Nullable, "TWString") | (TWPointerType::NullableMut, "TWString") => { + write!( + &mut return_string, + "\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()))?; + } + (TWPointerType::NonnullMut, "TWString") | (TWPointerType::Nonnull, "TWString") => { + panic!("Nonnull TWString is not supported"); + } + (TWPointerType::NullableMut, "TWData") | (TWPointerType::Nullable, "TWData") => { + 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()))?; + } + (TWPointerType::NonnullMut, "TWData") | (TWPointerType::Nonnull, "TWData") => { + write!( + &mut return_string, + "\tconst Rust::TWDataWrapper result = Rust::{}{}\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()))?; + } + (TWPointerType::NullableMut, "TWPrivateKey") + | (TWPointerType::Nullable, "TWPrivateKey") => { + 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()))?; + } + (TWPointerType::NonnullMut, "TWPrivateKey") + | (TWPointerType::Nonnull, "TWPrivateKey") => { + panic!("Nonnull TWPrivateKey is not supported"); + } + (TWPointerType::NullableMut, "TWPublicKey") + | (TWPointerType::Nullable, "TWPublicKey") => { + write!( + &mut return_string, + "\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()))?; + } + (TWPointerType::NonnullMut, "TWPublicKey") + | (TWPointerType::Nonnull, "TWPublicKey") => { + panic!("Nonnull TWPublicKey is not supported"); + } + (pointer_type, class_name) => { + let wrapper_class_name = class_name.replace("TW", ""); + let null_return = match pointer_type { + TWPointerType::Nullable | TWPointerType::NullableMut => { + "if (!resultRaw) {{ return nullptr; }}\n" + } + _ => "", + }; + write!( + &mut return_string, + "\tauto* resultRaw = Rust::{}{}\n\ + {null_return}\ + \tconst {wrapper_class_name} resultWrapped(resultRaw);\n\ + \treturn new {class_name} {{ resultWrapped }};\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() + ) + .map_err(|e| BadFormat(e.to_string()))?; + } + }, + TWType::Standard(ty) => match ty.as_str() { + "void" => { + write!( + &mut return_string, + "\tRust::{}{}\n", + func.rust_name, + generate_function_call(&converted_args)?.as_str() + ) + .map_err(|e| BadFormat(e.to_string()))?; + } + _ => { + 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) } -fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> Result<(String, String)> { - match ty { - "TWString *_Nonnull" => { - let mut conversion_code = String::new(); - writeln!( - &mut conversion_code, - "\tauto& {name}String = *reinterpret_cast({name});\n\ - \tconst Rust::TWStringWrapper {name}RustStr = {name}String;" - ) - .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}RustStr.get()", name))) - } - "TWString *_Nullable" => { - let mut conversion_code = String::new(); - writeln!( - &mut conversion_code, - "\tRust::TWStringWrapper {name}RustStr;\n\ - \tif ({name} != nullptr) {{\n\ +fn generate_conversion_code_with_var_name(tw_type: TWType, name: &str) -> Result<(String, String)> { + match tw_type { + TWType::Pointer(ref pointer_type, ref ty) => match (pointer_type, ty.as_str()) { + (TWPointerType::Nonnull, "TWString") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto& {name}String = *reinterpret_cast({name});\n\ + \tconst Rust::TWStringWrapper {name}RustStr = {name}String;" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustStr.get()", name))) + } + (TWPointerType::Nullable, "TWString") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\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({name});\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}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustStr.get()", name))) + } + (TWPointerType::Nonnull, "TWData") => { + 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))) + } + (TWPointerType::Nullable, "TWData") => { + 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))) + } + (TWPointerType::Nonnull, "TWPrivateKey") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto &{name}PrivateKey = *reinterpret_cast({name});\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))) + } + (TWPointerType::Nullable, "TWPrivateKey") => { + 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({name});\n\ - \tconst auto {name}PublicKeyType = static_cast({name}PublicKey.type);\n\ - \tauto* {name}RustRaw = Rust::tw_public_key_create_with_data({name}PublicKey.bytes.data(), {name}PublicKey.bytes.size(), {name}PublicKeyType);\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}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPrivateKey.get()", name))) + } + (TWPointerType::Nonnull, "TWPublicKey") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tauto &{name}PublicKey = *reinterpret_cast({name});\n\ + \tconst auto {name}PublicKeyType = static_cast({name}PublicKey.type);\n\ + \tauto* {name}RustRaw = Rust::tw_public_key_create_with_data({name}PublicKey.bytes.data(), {name}PublicKey.bytes.size(), {name}PublicKeyType);\n\ + \tconst auto {name}RustPublicKey = Rust::wrapTWPublicKey({name}RustRaw);" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPublicKey.get()", name))) + } + (TWPointerType::Nullable, "TWPublicKey") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tstd::shared_ptr {name}RustPublicKey;\n\ + \tif ({name} != nullptr) {{\n\ \t\tconst auto& {name}PublicKey = {name};\n\ \t\tconst auto {name}PublicKeyType = static_cast({name}PublicKey->impl.type);\n\ \t\tauto* {name}RustRaw = Rust::tw_public_key_create_with_data({name}PublicKey->impl.bytes.data(), {name}PublicKey->impl.bytes.size(), {name}PublicKeyType);\n\ \t\t{name}RustPublicKey = Rust::wrapTWPublicKey({name}RustRaw);\n\ - \t}}" - ) - .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}RustPublicKey.get()", name))) - } - "struct TWDataVector *_Nonnull" => { - let mut conversion_code = String::new(); - writeln!( - &mut conversion_code, - "\tconst Rust::TWDataVectorWrapper {name}RustDataVector = createFromTWDataVector({name});" - ) - .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}RustDataVector.get()", name))) - } - "struct TWDataVector *_Nullable" => { - let mut conversion_code = String::new(); - writeln!( - &mut conversion_code, - "\tstd::shared_ptr {name}RustDataVector;\n\ - \tif ({name} != nullptr) {{\n\ + \t}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustPublicKey.get()", name))) + } + (TWPointerType::Nonnull, "TWDataVector") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tconst Rust::TWDataVectorWrapper {name}RustDataVector = createFromTWDataVector({name});" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustDataVector.get()", name))) + } + (TWPointerType::Nullable, "TWDataVector") => { + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + "\tstd::shared_ptr {name}RustDataVector;\n\ + \tif ({name} != nullptr) {{\n\ \t\t{name}RustDataVector = createFromTWDataVector({name});\n\ - \t}}" - ) - .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}RustDataVector.get()", name))) - } - _ => Ok(("".to_string(), name.to_string())), + \t}}" + ) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustDataVector.get()", name))) + } + _ if tw_type.cpp_type().starts_with("struct ") => { + Ok(("".to_string(), format!("{name}->impl.ptr.get()"))) + } + _ => panic!("Unsupported type: {}", tw_type.cpp_type()), + }, + TWType::Standard(_) => Ok(("".to_string(), name.to_string())), } } fn generate_function_definition( file: &mut std::fs::File, info: &TWConfig, - func: &TWStaticFunction, + func_type: TWFunctionType, + func: &TWFunction, ) -> Result<()> { - let mut func_def = generate_function_signature(&info.class, func, false)?; + let mut func_def = generate_function_signature(&info.class, func_type, func, false)?; func_def += " {\n"; let mut converted_args = vec![]; for arg in func.args.iter() { - let func_type = convert_rust_type_to_cpp(&arg.ty); + let func_type = TWType::from(arg.ty.clone()); let (conversion_code, converted_arg) = - generate_conversion_code_with_var_name(&func_type, &arg.name.to_lower_camel_case())?; + generate_conversion_code_with_var_name(func_type, &arg.name.to_lower_camel_case())?; func_def += conversion_code.as_str(); converted_args.push(converted_arg); } @@ -456,6 +495,27 @@ fn generate_function_definition( Ok(()) } +fn generate_destructor_definition( + file: &mut std::fs::File, + info: &TWConfig, + destructor: &TWFunction, +) -> Result<()> { + let function_signature = + generate_function_signature(&info.class, TWFunctionType::Destructor, destructor, false)?; + assert!( + destructor.args.len() == 1, + "Destructor must have exactly one argument" + ); + let arg_name = &destructor.args[0].name.to_lower_camel_case(); + writeln!( + file, + "{function_signature}{{\n\ + \tdelete {arg_name};\n\ + }}" + )?; + Ok(()) +} + fn generate_source(info: &TWConfig) -> Result<()> { let file_path = format!("{SOURCE_OUT_DIR}/{}.cpp", info.class); let mut file = std::fs::File::create(&file_path)?; @@ -465,8 +525,11 @@ fn generate_source(info: &TWConfig) -> Result<()> { writeln!(file, "\nusing namespace TW;\n")?; - for func in &info.static_functions { - generate_function_definition(&mut file, info, func)?; + for (func_type, func) in info.functions(false) { + generate_function_definition(&mut file, info, func_type, func)?; + } + if let Some(destructor) = &info.destructor { + generate_destructor_definition(&mut file, info, destructor)?; } file.flush()?; @@ -490,6 +553,10 @@ pub fn generate_cpp_bindings() -> Result<()> { let info: TWConfig = serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); + if info.is_wrapped() { + generate_wrapper_header(&info)?; + } + generate_header(&info)?; generate_source(&info)?; } diff --git a/codegen-v2/src/codegen/cpp/code_gen_types.rs b/codegen-v2/src/codegen/cpp/code_gen_types.rs new file mode 100644 index 00000000000..555fa8b8e88 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/code_gen_types.rs @@ -0,0 +1,152 @@ +use regex::Regex; +use serde::{Deserialize, Serialize}; + +pub enum TWFunctionType { + StaticFunction, + Constructor, + Destructor, + Method, + Property, +} + +#[derive(Deserialize, Serialize, Debug, Default)] +pub struct TWConfig { + pub class: String, + pub static_functions: Vec, + pub constructors: Option>, + pub destructor: Option, + pub methods: Option>, + pub properties: Option>, +} + +impl TWConfig { + pub fn functions(&self, include_destructor: bool) -> Vec<(TWFunctionType, &TWFunction)> { + let mut functions = self + .static_functions + .iter() + .map(|f| (TWFunctionType::StaticFunction, f)) + .collect::>(); + if let Some(constructors) = &self.constructors { + functions.extend( + constructors + .iter() + .map(|f| (TWFunctionType::Constructor, f)), + ); + } + if let Some(methods) = &self.methods { + functions.extend(methods.iter().map(|f| (TWFunctionType::Method, f))); + } + if let Some(properties) = &self.properties { + functions.extend(properties.iter().map(|f| (TWFunctionType::Property, f))); + } + if include_destructor { + if let Some(destructor) = &self.destructor { + functions.push((TWFunctionType::Destructor, destructor)); + } + } + functions + } + + pub fn is_wrapped(&self) -> bool { + self.constructors.is_some() && self.destructor.is_some() + } +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWFunction { + pub name: String, + pub rust_name: String, + pub args: Vec, + pub return_type: String, + pub docs: Vec, +} + +impl TWFunction { + pub fn types(&self) -> Vec { + self.args + .iter() + .map(|arg| arg.ty.clone()) + .chain(std::iter::once(self.return_type.clone())) + .collect() + } +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct TWArg { + pub name: String, + pub ty: String, +} + +#[derive(Debug)] +pub enum TWPointerType { + Nonnull, + NonnullMut, + Nullable, + NullableMut, +} + +#[derive(Debug)] +pub enum TWType { + Pointer(TWPointerType, String), + Standard(String), +} + +impl From for TWType { + fn from(ty: String) -> Self { + let trimmed = ty.replace(" ", ""); + if let Some(captures) = Regex::new(r"^Nonnull<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + TWType::Pointer(TWPointerType::Nonnull, captures[1].to_string()) + } else if let Some(captures) = Regex::new(r"^NonnullMut<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + TWType::Pointer(TWPointerType::NonnullMut, captures[1].to_string()) + } else if let Some(captures) = Regex::new(r"^Nullable<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + TWType::Pointer(TWPointerType::Nullable, captures[1].to_string()) + } else if let Some(captures) = Regex::new(r"^NullableMut<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + TWType::Pointer(TWPointerType::NullableMut, captures[1].to_string()) + } else { + TWType::Standard(trimmed) + } + } +} + +impl TWType { + pub fn cpp_type(&self) -> String { + match self { + TWType::Standard(ty) => match ty.as_str() { + "u8" => "uint8_t".to_string(), + "u16" => "uint16_t".to_string(), + "u32" => "uint32_t".to_string(), + "u64" => "uint64_t".to_string(), + "i8" => "int8_t".to_string(), + "i16" => "int16_t".to_string(), + "i32" => "int32_t".to_string(), + "i64" => "int64_t".to_string(), + "TWFFICoinType" => "enum TWCoinType".to_string(), + _ => ty.to_string(), + }, + TWType::Pointer(pointer_type, ty) => { + let ty = match ty.as_str() { + "TWString" | "TWData" => ty.to_string(), + _ => format!("struct {}", ty), + }; + match pointer_type { + TWPointerType::Nonnull => format!("{} *_Nonnull", ty), + TWPointerType::NonnullMut => format!("{} *_Nonnull", ty), + TWPointerType::Nullable => format!("{} *_Nullable", ty), + TWPointerType::NullableMut => format!("{} *_Nullable", ty), + } + } + } + } +} diff --git a/codegen-v2/src/codegen/cpp/mod.rs b/codegen-v2/src/codegen/cpp/mod.rs index 147960dd30c..d066709dbaf 100644 --- a/codegen-v2/src/codegen/cpp/mod.rs +++ b/codegen-v2/src/codegen/cpp/mod.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; pub mod blockchain_dispatcher_generator; pub mod code_gen; +pub mod code_gen_types; pub mod entry_generator; pub mod new_blockchain; pub mod new_cosmos_chain; diff --git a/docs/registry.md b/docs/registry.md index 196c3d31653..2fcea9b71a2 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -133,7 +133,7 @@ This list is generated from [./registry.json](../registry.json) | 10004689 | IoTeX EVM | IOTX | | | | 10007000 | NativeZetaChain | ZETA | | | | 10007700 | NativeCanto | CANTO | | | -| 10008217 | Kaia | KAIA | | | +| 10008217 | Kaia | KAIA | | | | 10009000 | Avalanche C-Chain | AVAX | | | | 10009001 | Evmos | EVMOS | | | | 10042170 | Arbitrum Nova | ETH | | | diff --git a/include/TrustWalletCore/TWCryptoBoxPublicKey.h b/include/TrustWalletCore/TWCryptoBoxPublicKey.h deleted file mode 100644 index e46ea72feae..00000000000 --- a/include/TrustWalletCore/TWCryptoBoxPublicKey.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "TWBase.h" -#include "TWData.h" -#include "TWString.h" - -TW_EXTERN_C_BEGIN - -/// Public key used in `crypto_box` cryptography. -TW_EXPORT_CLASS -struct TWCryptoBoxPublicKey; - -/// Determines if the given public key is valid or not. -/// -/// \param data *non-null* byte array. -/// \return true if the public key is valid, false otherwise. -TW_EXPORT_STATIC_METHOD -bool TWCryptoBoxPublicKeyIsValid(TWData* _Nonnull data); - -/// Create a `crypto_box` public key with the given block of data. -/// -/// \param data *non-null* byte array. Expected to have 32 bytes. -/// \note Should be deleted with \tw_crypto_box_public_key_delete. -/// \return Nullable pointer to Public Key. -TW_EXPORT_STATIC_METHOD -struct TWCryptoBoxPublicKey* _Nullable TWCryptoBoxPublicKeyCreateWithData(TWData* _Nonnull data); - -/// Delete the given public key. -/// -/// \param publicKey *non-null* pointer to public key. -TW_EXPORT_METHOD -void TWCryptoBoxPublicKeyDelete(struct TWCryptoBoxPublicKey* _Nonnull publicKey); - -/// Returns the raw data of the given public-key. -/// -/// \param publicKey *non-null* pointer to a public key. -/// \return C-compatible result with a C-compatible byte array. -TW_EXPORT_PROPERTY -TWData* _Nonnull TWCryptoBoxPublicKeyData(struct TWCryptoBoxPublicKey* _Nonnull publicKey); - -TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCryptoBoxSecretKey.h b/include/TrustWalletCore/TWCryptoBoxSecretKey.h deleted file mode 100644 index f93ad92eb56..00000000000 --- a/include/TrustWalletCore/TWCryptoBoxSecretKey.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "TWBase.h" -#include "TWCryptoBoxPublicKey.h" -#include "TWData.h" -#include "TWString.h" - -TW_EXTERN_C_BEGIN - -/// Secret key used in `crypto_box` cryptography. -TW_EXPORT_CLASS -struct TWCryptoBoxSecretKey; - -/// Determines if the given secret key is valid or not. -/// -/// \param data *non-null* byte array. -/// \return true if the secret key is valid, false otherwise. -TW_EXPORT_STATIC_METHOD -bool TWCryptoBoxSecretKeyIsValid(TWData* _Nonnull data); - -/// Create a random secret key. -/// -/// \note Should be deleted with \tw_crypto_box_secret_key_delete. -/// \return *non-null* pointer to Secret Key. -TW_EXPORT_STATIC_METHOD -struct TWCryptoBoxSecretKey* _Nonnull TWCryptoBoxSecretKeyCreate(); - -/// Create a `crypto_box` secret key with the given block of data. -/// -/// \param data *non-null* byte array. Expected to have 32 bytes. -/// \note Should be deleted with \tw_crypto_box_secret_key_delete. -/// \return Nullable pointer to Secret Key. -TW_EXPORT_STATIC_METHOD -struct TWCryptoBoxSecretKey* _Nullable TWCryptoBoxSecretKeyCreateWithData(TWData* _Nonnull data); - -/// Delete the given secret `key`. -/// -/// \param key *non-null* pointer to secret key. -TW_EXPORT_METHOD -void TWCryptoBoxSecretKeyDelete(struct TWCryptoBoxSecretKey* _Nonnull key); - -/// Returns the public key associated with the given `key`. -/// -/// \param key *non-null* pointer to the private key. -/// \return *non-null* pointer to the corresponding public key. -TW_EXPORT_METHOD -struct TWCryptoBoxPublicKey* _Nonnull TWCryptoBoxSecretKeyGetPublicKey(struct TWCryptoBoxSecretKey* _Nonnull key); - -/// Returns the raw data of the given secret-key. -/// -/// \param secretKey *non-null* pointer to a secret key. -/// \return C-compatible result with a C-compatible byte array. -TW_EXPORT_PROPERTY -TWData* _Nonnull TWCryptoBoxSecretKeyData(struct TWCryptoBoxSecretKey* _Nonnull secretKey); - -TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fa5852beabf..b60b8a02478 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2093,6 +2093,7 @@ dependencies = [ "tw_encoding", "tw_hash", "tw_keypair", + "tw_macros", "tw_memory", "tw_misc", "zeroize", diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index d0e223475da..634b5ae8ea2 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -36,6 +36,7 @@ crypto_box = "0.9.1" # Starknet specific: starknet-crypto = "0.5.0" starknet-ff = "0.3.2" +tw_macros = { path = "../tw_macros" } [dev-dependencies] serde_json = "1.0" diff --git a/rust/tw_keypair/src/ffi/crypto_box/public_key.rs b/rust/tw_keypair/src/ffi/crypto_box/public_key.rs index 44caab07d63..3f1fac4e68e 100644 --- a/rust/tw_keypair/src/ffi/crypto_box/public_key.rs +++ b/rust/tw_keypair/src/ffi/crypto_box/public_key.rs @@ -5,8 +5,9 @@ #![allow(clippy::missing_safety_doc)] use crate::nacl_crypto_box::public_key::PublicKey; -use tw_memory::ffi::tw_data::TWData; -use tw_memory::ffi::RawPtrTrait; +use tw_macros::tw_ffi; +use tw_memory::ffi::{tw_data::TWData, Nonnull, NullableMut}; +use tw_memory::ffi::{NonnullMut, RawPtrTrait}; use tw_misc::{try_or_else, try_or_false}; /// Public key used in `crypto_box` cryptography. @@ -18,8 +19,9 @@ impl RawPtrTrait for TWCryptoBoxPublicKey {} /// /// \param data *non-null* byte array. /// \return true if the public key is valid, false otherwise. +#[tw_ffi(ty = static_function, class = TWCryptoBoxPublicKey, name = IsValid)] #[no_mangle] -pub unsafe extern "C" fn tw_crypto_box_public_key_is_valid(data: *const TWData) -> bool { +pub unsafe extern "C" fn tw_crypto_box_public_key_is_valid(data: Nonnull) -> bool { let bytes_ref = try_or_false!(TWData::from_ptr_as_ref(data)); PublicKey::try_from(bytes_ref.as_slice()).is_ok() } @@ -29,10 +31,11 @@ pub unsafe extern "C" fn tw_crypto_box_public_key_is_valid(data: *const TWData) /// \param data *non-null* byte array. Expected to have 32 bytes. /// \note Should be deleted with \tw_crypto_box_public_key_delete. /// \return Nullable pointer to Public Key. +#[tw_ffi(ty = constructor, class = TWCryptoBoxPublicKey, name = CreateWithData)] #[no_mangle] pub unsafe extern "C" fn tw_crypto_box_public_key_create_with_data( - data: *const TWData, -) -> *mut TWCryptoBoxPublicKey { + data: Nonnull, +) -> NullableMut { let bytes_ref = try_or_else!(TWData::from_ptr_as_ref(data), std::ptr::null_mut); let pubkey = try_or_else!( PublicKey::try_from(bytes_ref.as_slice()), @@ -44,8 +47,11 @@ pub unsafe extern "C" fn tw_crypto_box_public_key_create_with_data( /// Delete the given public key. /// /// \param public_key *non-null* pointer to public key. +#[tw_ffi(ty = destructor, class = TWCryptoBoxPublicKey, name = Delete)] #[no_mangle] -pub unsafe extern "C" fn tw_crypto_box_public_key_delete(public_key: *mut TWCryptoBoxPublicKey) { +pub unsafe extern "C" fn tw_crypto_box_public_key_delete( + public_key: NonnullMut, +) { // Take the ownership back to rust and drop the owner. let _ = TWCryptoBoxPublicKey::from_ptr(public_key); } @@ -54,10 +60,11 @@ pub unsafe extern "C" fn tw_crypto_box_public_key_delete(public_key: *mut TWCryp /// /// \param public_key *non-null* pointer to a public key. /// \return C-compatible result with a C-compatible byte array. +#[tw_ffi(ty = property, class = TWCryptoBoxPublicKey, name = Data)] #[no_mangle] pub unsafe extern "C" fn tw_crypto_box_public_key_data( - public_key: *const TWCryptoBoxPublicKey, -) -> *mut TWData { + public_key: Nonnull, +) -> NonnullMut { let pubkey_ref = try_or_else!( TWCryptoBoxPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut diff --git a/rust/tw_keypair/src/ffi/crypto_box/secret_key.rs b/rust/tw_keypair/src/ffi/crypto_box/secret_key.rs index c55503b9f88..95c81c6020f 100644 --- a/rust/tw_keypair/src/ffi/crypto_box/secret_key.rs +++ b/rust/tw_keypair/src/ffi/crypto_box/secret_key.rs @@ -6,8 +6,10 @@ use crate::ffi::crypto_box::public_key::TWCryptoBoxPublicKey; use crate::nacl_crypto_box::secret_key::SecretKey; +use tw_macros::tw_ffi; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::RawPtrTrait; +use tw_memory::ffi::{Nonnull, NonnullMut, NullableMut}; use tw_misc::traits::ToBytesZeroizing; use tw_misc::{try_or_else, try_or_false}; @@ -20,8 +22,9 @@ impl RawPtrTrait for TWCryptoBoxSecretKey {} /// /// \param data *non-null* byte array. /// \return true if the secret key is valid, false otherwise. +#[tw_ffi(ty = static_function, class = TWCryptoBoxSecretKey, name = IsValid)] #[no_mangle] -pub unsafe extern "C" fn tw_crypto_box_secret_key_is_valid(data: *const TWData) -> bool { +pub unsafe extern "C" fn tw_crypto_box_secret_key_is_valid(data: Nonnull) -> bool { let bytes_ref = try_or_false!(TWData::from_ptr_as_ref(data)); SecretKey::try_from(bytes_ref.as_slice()).is_ok() } @@ -30,8 +33,9 @@ pub unsafe extern "C" fn tw_crypto_box_secret_key_is_valid(data: *const TWData) /// /// \note Should be deleted with \tw_crypto_box_secret_key_delete. /// \return Nullable pointer to Private Key. +#[tw_ffi(ty = constructor, class = TWCryptoBoxSecretKey, name = Create)] #[no_mangle] -pub unsafe extern "C" fn tw_crypto_box_secret_key_create() -> *mut TWCryptoBoxSecretKey { +pub unsafe extern "C" fn tw_crypto_box_secret_key_create() -> NonnullMut { TWCryptoBoxSecretKey(SecretKey::random()).into_ptr() } @@ -40,10 +44,11 @@ pub unsafe extern "C" fn tw_crypto_box_secret_key_create() -> *mut TWCryptoBoxSe /// \param data *non-null* byte array. Expected to have 32 bytes. /// \note Should be deleted with \tw_crypto_box_secret_key_delete. /// \return Nullable pointer to Secret Key. +#[tw_ffi(ty = constructor, class = TWCryptoBoxSecretKey, name = CreateWithData)] #[no_mangle] pub unsafe extern "C" fn tw_crypto_box_secret_key_create_with_data( - data: *const TWData, -) -> *mut TWCryptoBoxSecretKey { + data: Nonnull, +) -> NullableMut { let bytes_ref = try_or_else!(TWData::from_ptr_as_ref(data), std::ptr::null_mut); let secret = try_or_else!( SecretKey::try_from(bytes_ref.as_slice()), @@ -55,8 +60,9 @@ pub unsafe extern "C" fn tw_crypto_box_secret_key_create_with_data( /// Delete the given secret `key`. /// /// \param key *non-null* pointer to secret key. +#[tw_ffi(ty = destructor, class = TWCryptoBoxSecretKey, name = Delete)] #[no_mangle] -pub unsafe extern "C" fn tw_crypto_box_secret_key_delete(key: *mut TWCryptoBoxSecretKey) { +pub unsafe extern "C" fn tw_crypto_box_secret_key_delete(key: NonnullMut) { // Take the ownership back to rust and drop the owner. let _ = TWCryptoBoxSecretKey::from_ptr(key); } @@ -65,10 +71,11 @@ pub unsafe extern "C" fn tw_crypto_box_secret_key_delete(key: *mut TWCryptoBoxSe /// /// \param key *non-null* pointer to the private key. /// \return *non-null* pointer to the corresponding public key. +#[tw_ffi(ty = method, class = TWCryptoBoxSecretKey, name = GetPublicKey)] #[no_mangle] pub unsafe extern "C" fn tw_crypto_box_secret_key_get_public_key( - key: *mut TWCryptoBoxSecretKey, -) -> *mut TWCryptoBoxPublicKey { + key: NonnullMut, +) -> NonnullMut { let secret = try_or_else!( TWCryptoBoxSecretKey::from_ptr_as_ref(key), std::ptr::null_mut @@ -80,10 +87,11 @@ pub unsafe extern "C" fn tw_crypto_box_secret_key_get_public_key( /// /// \param secret_key *non-null* pointer to a secret key. /// \return C-compatible result with a C-compatible byte array. +#[tw_ffi(ty = property, class = TWCryptoBoxSecretKey, name = Data)] #[no_mangle] pub unsafe extern "C" fn tw_crypto_box_secret_key_data( - secret_key: *const TWCryptoBoxSecretKey, -) -> *mut TWData { + secret_key: Nonnull, +) -> NonnullMut { let secret_ref = try_or_else!( TWCryptoBoxSecretKey::from_ptr_as_ref(secret_key), std::ptr::null_mut diff --git a/rust/tw_macros/src/code_gen.rs b/rust/tw_macros/src/code_gen.rs index 3b311486b9b..5170d5f58c5 100644 --- a/rust/tw_macros/src/code_gen.rs +++ b/rust/tw_macros/src/code_gen.rs @@ -1,13 +1,17 @@ use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Default)] pub struct TWConfig { pub class: String, - pub static_functions: Vec, + pub static_functions: Vec, + pub constructors: Option>, + pub destructor: Option, + pub methods: Option>, + pub properties: Option>, } #[derive(Deserialize, Serialize, Debug)] -pub struct TWStaticFunction { +pub struct TWFunction { pub name: String, pub rust_name: String, pub args: Vec, diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index fed4eb46e0a..983a3b3bee4 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -11,7 +11,7 @@ use std::fmt; use std::fs; use std::path::Path; -use crate::code_gen::{TWArg, TWConfig, TWStaticFunction}; +use crate::code_gen::{TWArg, TWConfig, TWFunction}; pub mod keywords { use syn::custom_keyword; @@ -20,11 +20,19 @@ pub mod keywords { custom_keyword!(class); custom_keyword!(name); custom_keyword!(static_function); + custom_keyword!(constructor); + custom_keyword!(destructor); + custom_keyword!(method); + custom_keyword!(property); } #[derive(Clone, Debug, PartialEq, Eq)] pub enum TWFFIType { StaticFunction, + Constructor, + Destructor, + Method, + Property, } impl Parse for TWFFIType { @@ -33,6 +41,18 @@ impl Parse for TWFFIType { if lookahead.peek(keywords::static_function) { input.parse::()?; Ok(Self::StaticFunction) + } else if lookahead.peek(keywords::constructor) { + input.parse::()?; + Ok(Self::Constructor) + } else if lookahead.peek(keywords::destructor) { + input.parse::()?; + Ok(Self::Destructor) + } else if lookahead.peek(keywords::method) { + input.parse::()?; + Ok(Self::Method) + } else if lookahead.peek(keywords::property) { + input.parse::()?; + Ok(Self::Property) } else { Err(lookahead.error()) } @@ -43,6 +63,10 @@ impl fmt::Display for TWFFIType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TWFFIType::StaticFunction => write!(f, "static_function"), + TWFFIType::Constructor => write!(f, "constructor"), + TWFFIType::Destructor => write!(f, "destructor"), + TWFFIType::Method => write!(f, "method"), + TWFFIType::Property => write!(f, "property"), } } } @@ -53,7 +77,7 @@ pub struct TWFFIAttrArgs { #[parse_if(_ty_keyword.is_some())] pub _eq: Option, #[parse_if(_ty_keyword.is_some())] - pub _ty: Option, + pub ty: Option, #[parse_if(_ty_keyword.is_some())] pub _comma: Option, @@ -72,6 +96,18 @@ pub struct TWFFIAttrArgs { pub name: Option, } +fn update_or_append_function(functions: &mut Option>, function: TWFunction) { + if let Some(funcs) = functions { + if let Some(idx) = funcs.iter().position(|f| f.name == function.name) { + funcs[idx] = function; + } else { + funcs.push(function); + } + } else { + *functions = Some(vec![function]); + } +} + pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { let args = parse2::(attr)?; @@ -116,7 +152,7 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { None }) .collect::>(); - let static_function = TWStaticFunction { + let function = TWFunction { name: args.name.unwrap().to_string(), rust_name: func_name, args: func_args, @@ -137,31 +173,47 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { let _ = fs::remove_file(&yaml_file_path); TWConfig { class, - static_functions: vec![], + ..Default::default() } }, }, Err(_) => TWConfig { class, - static_functions: vec![], + ..Default::default() }, } } else { TWConfig { class, - static_functions: vec![], + ..Default::default() } }; - if let Some(idx) = config - .static_functions - .iter() - .position(|f| f.name == static_function.name) - { - config.static_functions[idx] = static_function; - } else { - config.static_functions.push(static_function); + match args.ty { + Some(TWFFIType::StaticFunction) => { + if let Some(idx) = config + .static_functions + .iter() + .position(|f| f.name == function.name) + { + config.static_functions[idx] = function; + } else { + config.static_functions.push(function); + } + }, + Some(TWFFIType::Constructor) => { + update_or_append_function(&mut config.constructors, function); + }, + Some(TWFFIType::Destructor) => { + config.destructor = Some(function); + }, + Some(TWFFIType::Method) => { + update_or_append_function(&mut config.methods, function); + }, + Some(TWFFIType::Property) => { + update_or_append_function(&mut config.properties, function); + }, + _ => panic!("Invalid FFI type"), } - let yaml_output: String = serde_yaml::to_string(&config).expect("Failed to serialize to YAML"); fs::write(&yaml_file_path, yaml_output).expect("Failed to write YAML file"); diff --git a/src/interface/TWCryptoBoxPublicKey.cpp b/src/interface/TWCryptoBoxPublicKey.cpp deleted file mode 100644 index 3dc0e10ec08..00000000000 --- a/src/interface/TWCryptoBoxPublicKey.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "TrustWalletCore/TWCryptoBoxPublicKey.h" -#include "CryptoBox.h" - -using namespace TW; - -bool TWCryptoBoxPublicKeyIsValid(TWData* _Nonnull data) { - auto& bytes = *reinterpret_cast(data); - return CryptoBox::PublicKey::isValid(bytes); -} - -struct TWCryptoBoxPublicKey* _Nullable TWCryptoBoxPublicKeyCreateWithData(TWData* _Nonnull data) { - auto& bytes = *reinterpret_cast(data); - auto publicKey = CryptoBox::PublicKey::fromBytes(bytes); - if (!publicKey) { - return nullptr; - } - return new TWCryptoBoxPublicKey { publicKey.value() }; -} - -void TWCryptoBoxPublicKeyDelete(struct TWCryptoBoxPublicKey* _Nonnull publicKey) { - delete publicKey; -} - -TWData* _Nonnull TWCryptoBoxPublicKeyData(struct TWCryptoBoxPublicKey* _Nonnull publicKey) { - auto bytes = publicKey->impl.getData(); - return TWDataCreateWithBytes(bytes.data(), bytes.size()); -} diff --git a/src/interface/TWCryptoBoxSecretKey.cpp b/src/interface/TWCryptoBoxSecretKey.cpp deleted file mode 100644 index 0a16fa6a06f..00000000000 --- a/src/interface/TWCryptoBoxSecretKey.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "TrustWalletCore/TWCryptoBoxSecretKey.h" -#include "CryptoBox.h" - -using namespace TW; - -bool TWCryptoBoxSecretKeyIsValid(TWData* _Nonnull data) { - auto& bytes = *reinterpret_cast(data); - return CryptoBox::SecretKey::isValid(bytes); -} - -struct TWCryptoBoxSecretKey* _Nullable TWCryptoBoxSecretKeyCreateWithData(TWData* _Nonnull data) { - auto& bytes = *reinterpret_cast(data); - auto secretKey = CryptoBox::SecretKey::fromBytes(bytes); - if (!secretKey) { - return nullptr; - } - return new TWCryptoBoxSecretKey { secretKey.value() }; -} - -struct TWCryptoBoxSecretKey* _Nonnull TWCryptoBoxSecretKeyCreate() { - CryptoBox::SecretKey secretKey; - return new TWCryptoBoxSecretKey { secretKey }; -} - -void TWCryptoBoxSecretKeyDelete(struct TWCryptoBoxSecretKey* _Nonnull key) { - delete key; -} - -struct TWCryptoBoxPublicKey* TWCryptoBoxSecretKeyGetPublicKey(struct TWCryptoBoxSecretKey* _Nonnull key) { - auto publicKey = key->impl.getPublicKey(); - return new TWCryptoBoxPublicKey { publicKey }; -} - -TWData* _Nonnull TWCryptoBoxSecretKeyData(struct TWCryptoBoxSecretKey* _Nonnull secretKey) { - auto bytes = secretKey->impl.getData(); - return TWDataCreateWithBytes(bytes.data(), bytes.size()); -}