From ff46b59a2e0176d2df916943b06f864fd6f7df9d Mon Sep 17 00:00:00 2001 From: dcorral Date: Tue, 11 Aug 2026 18:12:50 +0200 Subject: [PATCH 1/4] c-ffi: add missing begin/end exports --- bindings/c-ffi/src/lib.rs | 76 +++++++++++++++++++++++++++++ bindings/c-ffi/src/utils.rs | 96 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/bindings/c-ffi/src/lib.rs b/bindings/c-ffi/src/lib.rs index c1d6a07e..6aed6c56 100644 --- a/bindings/c-ffi/src/lib.rs +++ b/bindings/c-ffi/src/lib.rs @@ -188,6 +188,26 @@ pub extern "C" fn rgblib_delete_transfers( delete_transfers(wallet, batch_transfer_idx_opt, no_asset_only).into() } +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_drain_to_begin( + wallet: &COpaqueStruct, + online: *const c_char, + address: *const c_char, + fee_rate: *const c_char, + dry_run: bool, +) -> CResultString { + drain_to_begin(wallet, online, address, fee_rate, dry_run).into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_drain_to_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> CResultString { + drain_to_end(wallet, online, signed_psbt).into() +} + #[unsafe(no_mangle)] pub extern "C" fn rgblib_fail_transfers( wallet: &COpaqueStruct, @@ -289,6 +309,37 @@ pub extern "C" fn rgblib_inflate( .into() } +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_inflate_begin( + wallet: &COpaqueStruct, + online: *const c_char, + asset_id: *const c_char, + inflation_amounts: *const c_char, + fee_rate: *const c_char, + min_confirmations: *const c_char, + dry_run: bool, +) -> CResultString { + inflate_begin( + wallet, + online, + asset_id, + inflation_amounts, + fee_rate, + min_confirmations, + dry_run, + ) + .into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_inflate_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> CResultString { + inflate_end(wallet, online, signed_psbt).into() +} + #[unsafe(no_mangle)] pub extern "C" fn rgblib_invoice_data(invoice_string: *const c_char) -> CResultString { invoice_data(invoice_string).into() @@ -498,6 +549,31 @@ pub extern "C" fn rgblib_send_btc( send_btc(wallet, online, address, amount, fee_rate, skip_sync).into() } +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_send_btc_begin( + wallet: &COpaqueStruct, + online: *const c_char, + address: *const c_char, + amount: *const c_char, + fee_rate: *const c_char, + skip_sync: bool, + dry_run: bool, +) -> CResultString { + send_btc_begin( + wallet, online, address, amount, fee_rate, skip_sync, dry_run, + ) + .into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn rgblib_send_btc_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> CResultString { + send_btc_end(wallet, online, signed_psbt).into() +} + #[unsafe(no_mangle)] pub extern "C" fn rgblib_send_end( wallet: &COpaqueStruct, diff --git a/bindings/c-ffi/src/utils.rs b/bindings/c-ffi/src/utils.rs index ece36cf8..7772fb86 100644 --- a/bindings/c-ffi/src/utils.rs +++ b/bindings/c-ffi/src/utils.rs @@ -327,6 +327,33 @@ pub(crate) fn delete_transfers( Ok(serde_json::to_string(&res)?) } +pub(crate) fn drain_to_begin( + wallet: &COpaqueStruct, + online: *const c_char, + address: *const c_char, + fee_rate: *const c_char, + dry_run: bool, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let address = ptr_to_string(address); + let fee_rate = ptr_to_num(fee_rate)?; + let res = wallet.drain_to_begin(online, address, fee_rate, dry_run)?; + Ok(res) +} + +pub(crate) fn drain_to_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let signed_psbt = ptr_to_string(signed_psbt); + let res = wallet.drain_to_end(online, signed_psbt)?; + Ok(res) +} + pub(crate) fn fail_transfers( wallet: &COpaqueStruct, online: *const c_char, @@ -442,6 +469,45 @@ pub(crate) fn inflate( Ok(serde_json::to_string(&res)?) } +#[allow(clippy::too_many_arguments)] +pub(crate) fn inflate_begin( + wallet: &COpaqueStruct, + online: *const c_char, + asset_id: *const c_char, + inflation_amounts: *const c_char, + fee_rate: *const c_char, + min_confirmations: *const c_char, + dry_run: bool, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let asset_id = ptr_to_string(asset_id); + let inflation_amounts = convert_strings_array(inflation_amounts)?; + let fee_rate = ptr_to_num(fee_rate)?; + let min_confirmations = ptr_to_num(min_confirmations)?; + let res = wallet.inflate_begin( + online, + asset_id, + inflation_amounts, + fee_rate, + min_confirmations, + dry_run, + )?; + Ok(serde_json::to_string(&res)?) +} + +pub(crate) fn inflate_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let signed_psbt = ptr_to_string(signed_psbt); + let res = wallet.inflate_end(online, signed_psbt)?; + Ok(serde_json::to_string(&res)?) +} + pub(crate) fn invoice_data(invoice_string: *const c_char) -> Result { let invoice_string = ptr_to_string(invoice_string); let invoice = rgb_lib::wallet::Invoice::new(invoice_string)?; @@ -715,6 +781,36 @@ pub(crate) fn send_btc( Ok(res) } +pub(crate) fn send_btc_begin( + wallet: &COpaqueStruct, + online: *const c_char, + address: *const c_char, + amount: *const c_char, + fee_rate: *const c_char, + skip_sync: bool, + dry_run: bool, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let address = ptr_to_string(address); + let amount = ptr_to_num(amount)?; + let fee_rate = ptr_to_num(fee_rate)?; + let res = wallet.send_btc_begin(online, address, amount, fee_rate, skip_sync, dry_run)?; + Ok(res) +} + +pub(crate) fn send_btc_end( + wallet: &COpaqueStruct, + online: *const c_char, + signed_psbt: *const c_char, +) -> Result { + let wallet = Wallet::from_opaque(wallet)?; + let online = convert_online(online)?; + let signed_psbt = ptr_to_string(signed_psbt); + let res = wallet.send_btc_end(online, signed_psbt)?; + Ok(res) +} + pub(crate) fn send_end( wallet: &COpaqueStruct, online: *const c_char, From 3ddd514a44aa92142cb864a2dc3e290faca35fe5 Mon Sep 17 00:00:00 2001 From: dcorral Date: Tue, 11 Aug 2026 18:13:08 +0200 Subject: [PATCH 2/4] c-ffi: fix list_transfers call in example --- bindings/c-ffi/example.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/c-ffi/example.c b/bindings/c-ffi/example.c index 3a58e38b..e36f340f 100644 --- a/bindings/c-ffi/example.c +++ b/bindings/c-ffi/example.c @@ -180,7 +180,8 @@ int main() { CResultString fee_res = rgblib_get_fee_estimation(wlt, online, "7"); printf("Fee estimation: %s\n", fee_res.inner); - CResultString transfers_res = rgblib_list_transfers(wlt, NULL); + CResultString transfers_res = + rgblib_list_transfers(wlt, "\"AnyOrNone\"", NULL); if (transfers_res.result == Err) { printf("ERR: %s\n", transfers_res.inner); return EXIT_FAILURE; From c783d80e6a692df74a58b65c3a5c23ec9cd2b48e Mon Sep 17 00:00:00 2001 From: dcorral Date: Tue, 11 Aug 2026 18:19:51 +0200 Subject: [PATCH 3/4] c-ffi: add memory leak check to example --- bindings/c-ffi/example.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/bindings/c-ffi/example.c b/bindings/c-ffi/example.c index e36f340f..5880f7fd 100644 --- a/bindings/c-ffi/example.c +++ b/bindings/c-ffi/example.c @@ -1,9 +1,26 @@ #include "rgblib.h" #include #include +#include #include -int main() { +/* Run this method and monitor memory usage to check there are no memory leaks + */ +void checkMemoryLeak() { + for (int i = 0; i < 50; i++) { + CResultString keys_res = rgblib_generate_keys("Regtest", "Taproot"); + if (keys_res.result == Err) { + printf("ERR: %s\n", keys_res.inner); + return; + } + } +} + +int main(int argc, char *argv[]) { + if (argc > 1 && strcmp(argv[1], "checkMemoryLeak") == 0) { + checkMemoryLeak(); + return EXIT_SUCCESS; + } const char *bitcoin_network = "Regtest"; const char *witness_version = "Taproot"; CResultString keys_res = From fd1aca2b44f5b76ce86a7725583267fb9e9dd741 Mon Sep 17 00:00:00 2001 From: dcorral Date: Tue, 11 Aug 2026 18:20:36 +0200 Subject: [PATCH 4/4] c-ffi: add function to free returned strings --- bindings/c-ffi/example.c | 1 + bindings/c-ffi/src/lib.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/bindings/c-ffi/example.c b/bindings/c-ffi/example.c index 5880f7fd..8b419167 100644 --- a/bindings/c-ffi/example.c +++ b/bindings/c-ffi/example.c @@ -13,6 +13,7 @@ void checkMemoryLeak() { printf("ERR: %s\n", keys_res.inner); return; } + free_string(keys_res.inner); } } diff --git a/bindings/c-ffi/src/lib.rs b/bindings/c-ffi/src/lib.rs index 6aed6c56..0db42597 100644 --- a/bindings/c-ffi/src/lib.rs +++ b/bindings/c-ffi/src/lib.rs @@ -45,6 +45,17 @@ pub struct CResultString { inner: *mut c_char, } +#[allow(clippy::not_unsafe_ptr_arg_deref)] +#[unsafe(no_mangle)] +pub extern "C" fn free_string(ptr: *mut c_char) { + if ptr.is_null() { + return; + } + unsafe { + let _ = CString::from_raw(ptr); + } +} + #[unsafe(no_mangle)] pub extern "C" fn free_wallet(obj: COpaqueStruct) { unsafe {