From 7d914961322f7c206fd298f2e749a7b7c09f357c Mon Sep 17 00:00:00 2001 From: MoCipher Date: Fri, 20 Feb 2026 09:58:56 +0100 Subject: [PATCH] feat(uniffi): add UniFFI bridge functions + tests (generate_mnemonic, first_receive_address, create_psbt, sign_psbt) --- core/src/lib.rs | 3 +++ core/src/uniffi.rs | 44 +++++++++++++++++++++++++++++++ core/tests/uniffi_bridge_tests.rs | 20 ++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 core/src/uniffi.rs create mode 100644 core/tests/uniffi_bridge_tests.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index beb1683..435aba0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,9 @@ pub use swap_manager::SwapOffer; pub use swap_sim::SwapSimulation; pub use wallet_manager::{register_decrypted_db, close_decrypted_db, register_inmemory_db, close_inmemory_db, close_all, list_handles}; +mod uniffi; +pub use uniffi::{generate_mnemonic, first_receive_address, create_psbt, sign_psbt}; + /// WalletCore: minimal demonstrative API pub struct WalletCore { // In a real implementation, secrets should be stored in a secure enclave or encrypted storage diff --git a/core/src/uniffi.rs b/core/src/uniffi.rs new file mode 100644 index 0000000..7d3265c --- /dev/null +++ b/core/src/uniffi.rs @@ -0,0 +1,44 @@ +use crate::bip39::Mnemonic as LocalMnemonic; +use crate::wallet_bdk::{BdkWallet, PersistentBdkWallet}; +use bitcoin::Network; + +/// UniFFI-compatible thin bridge functions consumed by mobile/native bindings. +/// These mirror the `cryptec.udl` UDL surface and delegate to existing core logic. + +/// Generate a BIP39 mnemonic (wordlist phrase) +pub fn generate_mnemonic(strength: u32) -> String { + // Accept strengths like 128, 160, 192, 224, 256 + let s = match strength { + 128 | 160 | 192 | 224 | 256 => strength as usize, + _ => 128, + }; + let m = LocalMnemonic::generate(s); + m.phrase +} + +/// Return the first (external 0) receive address for the given mnemonic (Testnet) +pub fn first_receive_address(mnemonic: &str) -> String { + // Reuse the BDK wallet helper which already derives descriptors/addresses + let w = match BdkWallet::new_from_mnemonic(mnemonic, "", Network::Testnet) { + Ok(x) => x, + Err(_) => return String::new(), + }; + match w.get_new_address() { + Ok(a) => a, + Err(_) => String::new(), + } +} + +/// Create a PSBT (base64) using the mnemonic (Testnet) +pub fn create_psbt(mnemonic: &str, to_address: &str, satoshis: u64) -> String { + let w = BdkWallet::new_from_mnemonic(mnemonic, "", Network::Testnet) + .expect("create wallet"); + w.create_psbt(to_address, satoshis).expect("create psbt") +} + +/// Sign a PSBT (base64) with the wallet's internal keys (Testnet) +pub fn sign_psbt(mnemonic: &str, psbt_b64: &str) -> String { + let w = BdkWallet::new_from_mnemonic(mnemonic, "", Network::Testnet) + .expect("create wallet"); + w.sign_psbt_base64(psbt_b64).expect("sign psbt") +} diff --git a/core/tests/uniffi_bridge_tests.rs b/core/tests/uniffi_bridge_tests.rs new file mode 100644 index 0000000..d20b7c0 --- /dev/null +++ b/core/tests/uniffi_bridge_tests.rs @@ -0,0 +1,20 @@ +use crate::uniffi::{generate_mnemonic, first_receive_address, create_psbt, sign_psbt}; + +#[test] +fn uniffi_generate_and_address() { + let m = generate_mnemonic(128); + assert!(!m.is_empty()); + let addr = first_receive_address(&m); + assert!(!addr.is_empty()); +} + +#[test] +fn uniffi_psbt_roundtrip_mock() { + let m = generate_mnemonic(128); + let addr = first_receive_address(&m); + assert!(!addr.is_empty()); + let psbt = create_psbt(&m, &addr, 1000); + assert!(!psbt.is_empty()); + let signed = sign_psbt(&m, &psbt); + assert!(!signed.is_empty()); +}