Skip to content

Commit

Permalink
Adds support for complex custom types in Rust <> CPP FFI Generator (t…
Browse files Browse the repository at this point in the history
…rustwallet#4294)

* Adds support for complex custom types in Rust <> CPP FFI Generator

* Adds method and property in tw_ffi macro

* Addresses review comments
  • Loading branch information
gupnik authored Mar 5, 2025
1 parent 9ede115 commit e384f67
Show file tree
Hide file tree
Showing 15 changed files with 632 additions and 513 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/
Expand Down
667 changes: 367 additions & 300 deletions codegen-v2/src/codegen/cpp/code_gen.rs

Large diffs are not rendered by default.

152 changes: 152 additions & 0 deletions codegen-v2/src/codegen/cpp/code_gen_types.rs
Original file line number Diff line number Diff line change
@@ -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<TWFunction>,
pub constructors: Option<Vec<TWFunction>>,
pub destructor: Option<TWFunction>,
pub methods: Option<Vec<TWFunction>>,
pub properties: Option<Vec<TWFunction>>,
}

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::<Vec<_>>();
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<TWArg>,
pub return_type: String,
pub docs: Vec<String>,
}

impl TWFunction {
pub fn types(&self) -> Vec<String> {
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<String> 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),
}
}
}
}
}
1 change: 1 addition & 0 deletions codegen-v2/src/codegen/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion docs/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ This list is generated from [./registry.json](../registry.json)
| 10004689 | IoTeX EVM | IOTX | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/iotexevm/info/logo.png" width="32" /> | <https://iotex.io/> |
| 10007000 | NativeZetaChain | ZETA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/zetachain/info/logo.png" width="32" /> | <https://www.zetachain.com/> |
| 10007700 | NativeCanto | CANTO | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/nativecanto/info/logo.png" width="32" /> | <https://canto.io/> |
| 10008217 | Kaia | KAIA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/klaytn/info/logo.png" width="32" /> | <https://kaia.io> |
| 10008217 | Kaia | KAIA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kaia/info/logo.png" width="32" /> | <https://kaia.io> |
| 10009000 | Avalanche C-Chain | AVAX | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/avalanchec/info/logo.png" width="32" /> | <https://www.avalabs.org/> |
| 10009001 | Evmos | EVMOS | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/evmos/info/logo.png" width="32" /> | <https://evmos.org/> |
| 10042170 | Arbitrum Nova | ETH | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrumnova/info/logo.png" width="32" /> | <https://nova.arbitrum.io> |
Expand Down
45 changes: 0 additions & 45 deletions include/TrustWalletCore/TWCryptoBoxPublicKey.h

This file was deleted.

60 changes: 0 additions & 60 deletions include/TrustWalletCore/TWCryptoBoxSecretKey.h

This file was deleted.

1 change: 1 addition & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/tw_keypair/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
23 changes: 15 additions & 8 deletions rust/tw_keypair/src/ffi/crypto_box/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<TWData>) -> bool {
let bytes_ref = try_or_false!(TWData::from_ptr_as_ref(data));
PublicKey::try_from(bytes_ref.as_slice()).is_ok()
}
Expand All @@ -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<TWData>,
) -> NullableMut<TWCryptoBoxPublicKey> {
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()),
Expand All @@ -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<TWCryptoBoxPublicKey>,
) {
// Take the ownership back to rust and drop the owner.
let _ = TWCryptoBoxPublicKey::from_ptr(public_key);
}
Expand All @@ -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<TWCryptoBoxPublicKey>,
) -> NonnullMut<TWData> {
let pubkey_ref = try_or_else!(
TWCryptoBoxPublicKey::from_ptr_as_ref(public_key),
std::ptr::null_mut
Expand Down
Loading

0 comments on commit e384f67

Please sign in to comment.