diff --git a/bdk-kotlin/jvm/build.gradle b/bdk-kotlin/jvm/build.gradle index d356464..9640d69 100644 --- a/bdk-kotlin/jvm/build.gradle +++ b/bdk-kotlin/jvm/build.gradle @@ -4,6 +4,12 @@ plugins { id 'maven-publish' } +test { + testLogging { + events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR" + } +} + dependencies { implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Error.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Error.kt new file mode 100644 index 0000000..aeda367 --- /dev/null +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Error.kt @@ -0,0 +1,43 @@ +package org.bitcoindevkit.bdk + +enum class Error { + InvalidU32Bytes, + Generic, + ScriptDoesntHaveAddressForm, + SingleRecipientMultipleOutputs, + SingleRecipientNoInputs, + NoRecipients, + NoUtxosSelected, + OutputBelowDustLimit, + InsufficientFunds, + BnBTotalTriesExceeded, + BnBNoExactMatch, + UnknownUtxo, + TransactionNotFound, + TransactionConfirmed, + IrreplaceableTransaction, + FeeRateTooLow, + FeeTooLow, + MissingKeyOrigin, + Key, + ChecksumMismatch, + SpendingPolicyRequired, + InvalidPolicyPathError, + Signer, + InvalidProgressValue, + ProgressUpdateError, + InvalidOutpoint, + Descriptor, + AddressValidator, + Encode, + Miniscript, + Bip32, + Secp256k1, + Json, + Hex, + Psbt, + Electrum, +// Esplora +// CompactFilters + Sled, +} \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/LibJna.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/LibJna.kt index 411de5b..d186ef5 100644 --- a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/LibJna.kt +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/LibJna.kt @@ -35,10 +35,16 @@ interface LibJna : Library { // void free_string_result ( // StringResult_t * string_result); fun free_string_result(string_result: StringResult_t) - - // void free_string ( - // char * string); - fun free_string(string: Pointer?) + + // typedef struct WalletRef WalletRef_t; + class WalletRef_t : PointerType { + constructor() : super() + constructor(pointer: Pointer) : super(pointer) + } + + // void free_wallet_ref ( + // WalletRef_t * wallet_ref); + fun free_wallet_ref(wallet_ref: WalletRef_t) // typedef struct WalletResult WalletResult_t; class WalletResult_t : PointerType { @@ -58,17 +64,25 @@ interface LibJna : Library { // char * get_wallet_err ( // WalletResult_t const * wallet_result); - // TODO + fun get_wallet_err(wallet_result: WalletResult_t): Pointer? + + // WalletRef_t * get_wallet_ok ( + // WalletResult_t const * wallet_result); + fun get_wallet_ok(wallet_result: WalletResult_t): WalletRef_t? // VoidResult_t * sync_wallet ( - // WalletResult_t const * wallet_result); - fun sync_wallet(wallet_result: WalletResult_t): VoidResult_t + // WalletRef_t const * wallet_ref); + fun sync_wallet(wallet_ref: Pointer): VoidResult_t // StringResult_t * new_address ( - // WalletResult_t const * wallet_result); - fun new_address(wallet_result: WalletResult_t): StringResult_t + // WalletRef_t const * wallet_ref); + fun new_address(wallet_ref: Pointer): StringResult_t // void free_wallet_result ( // WalletResult_t * wallet_result); fun free_wallet_result(wallet_result: WalletResult_t) + + // void free_string ( + // char * string); + fun free_string(string: Pointer?) } diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/StringResult.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/StringResult.kt new file mode 100644 index 0000000..14e8c5a --- /dev/null +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/StringResult.kt @@ -0,0 +1,32 @@ +package org.bitcoindevkit.bdk + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class StringResult internal constructor(private val stringResultT: LibJna.StringResult_t) : LibBase() { + + private val log: Logger = LoggerFactory.getLogger(StringResult::class.java) + + fun isErr(): Boolean { + return libJna.get_string_err(stringResultT) != null + } + + fun err(): String? { + val errPointer = libJna.get_string_err(stringResultT) + val err = errPointer?.getString(0) + libJna.free_string(errPointer) + return err + } + + fun ok(): String? { + val okPointer = libJna.get_string_ok(stringResultT) + val ok = okPointer?.getString(0) + libJna.free_string(okPointer) + return ok + } + + protected fun finalize() { + libJna.free_string_result(stringResultT) + log.debug("StringResult_t freed") + } +} \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/VoidResult.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/VoidResult.kt new file mode 100644 index 0000000..50b1c86 --- /dev/null +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/VoidResult.kt @@ -0,0 +1,25 @@ +package org.bitcoindevkit.bdk + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class VoidResult internal constructor(private val voidResultT: LibJna.VoidResult_t) : LibBase() { + + private val log: Logger = LoggerFactory.getLogger(VoidResult::class.java) + + fun isErr(): Boolean { + return libJna.get_void_err(voidResultT) != null + } + + fun err(): String? { + val errPointer = libJna.get_void_err(voidResultT) + val err = errPointer?.getString(0) + libJna.free_string(errPointer) + return err + } + + protected fun finalize() { + libJna.free_void_result(voidResultT) + log.debug("VoidResult_t freed") + } +} \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Wallet.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Wallet.kt new file mode 100644 index 0000000..5ae9878 --- /dev/null +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/Wallet.kt @@ -0,0 +1,25 @@ +package org.bitcoindevkit.bdk + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class Wallet internal constructor( + private val walletRefT: LibJna.WalletRef_t, +) : LibBase() { + + private val log: Logger = LoggerFactory.getLogger(Wallet::class.java) + + fun sync(): VoidResult { + return VoidResult(libJna.sync_wallet(walletRefT.pointer)) + } + + fun getAddress(): StringResult { + return StringResult(libJna.new_address(walletRefT.pointer)) + } + + protected fun finalize() { + libJna.free_wallet_ref(walletRefT) + log.debug("WalletRef_t freed") + } + +} \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/WalletResult.kt b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/WalletResult.kt new file mode 100644 index 0000000..2ac325d --- /dev/null +++ b/bdk-kotlin/jvm/src/main/java/org/bitcoindevkit/bdk/WalletResult.kt @@ -0,0 +1,36 @@ +package org.bitcoindevkit.bdk + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class WalletResult( + name: String, + descriptor: String, + changeDescriptor: String?, +) : LibBase() { + + private val log: Logger = LoggerFactory.getLogger(WalletResult::class.java) + private val walletResultT = libJna.new_wallet_result(name, descriptor, changeDescriptor) + + fun isErr(): Boolean { + return libJna.get_wallet_err(walletResultT) != null + } + + fun err(): String? { + val errPointer = libJna.get_wallet_err(walletResultT) + val err = errPointer?.getString(0) + libJna.free_string(errPointer) + return err + } + + fun ok(): Wallet? { + val okWalletRef = libJna.get_wallet_ok(walletResultT) + return if (okWalletRef != null) Wallet(okWalletRef) else null + } + + protected fun finalize() { + libJna.free_wallet_result(walletResultT) + log.debug("WalletResult_t freed") + } + +} \ No newline at end of file diff --git a/bdk-kotlin/test-fixtures/build.gradle b/bdk-kotlin/test-fixtures/build.gradle index 46b7292..6c6b4ca 100644 --- a/bdk-kotlin/test-fixtures/build.gradle +++ b/bdk-kotlin/test-fixtures/build.gradle @@ -9,6 +9,7 @@ dependencies { implementation "net.java.dev.jna:jna:5.8.0" implementation (project(':jvm')) implementation 'org.jetbrains.kotlin:kotlin-test-junit' + //implementation "org.mockito.kotlin:mockito-kotlin:3.2.0" api "org.slf4j:slf4j-api:1.7.30" } diff --git a/bdk-kotlin/test-fixtures/src/main/java/org/bitcoindevkit/bdk/LibTest.kt b/bdk-kotlin/test-fixtures/src/main/java/org/bitcoindevkit/bdk/LibTest.kt index 7bb236f..49e6d85 100644 --- a/bdk-kotlin/test-fixtures/src/main/java/org/bitcoindevkit/bdk/LibTest.kt +++ b/bdk-kotlin/test-fixtures/src/main/java/org/bitcoindevkit/bdk/LibTest.kt @@ -1,71 +1,67 @@ package org.bitcoindevkit.bdk import org.junit.* -import org.junit.Assert.assertEquals +import org.junit.Assert.* import org.slf4j.Logger import org.slf4j.LoggerFactory -import kotlin.test.assertNotNull -import kotlin.test.assertNull /** * Library test, which will execute on linux host. * */ -abstract class LibTest { +abstract class LibTest : LibBase() { - companion object : LibBase() { - private val log: Logger = LoggerFactory.getLogger(LibTest::class.java) - - private lateinit var wallet_result: LibJna.WalletResult_t + private val log: Logger = LoggerFactory.getLogger(LibTest::class.java) - @BeforeClass - @JvmStatic - fun new_wallet() { - val name = "test_wallet" - val desc = - "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" - val change = - "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" + val name = "test_wallet" + val desc = + "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" + val change = + "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" - wallet_result = libJna.new_wallet_result(name, desc, change) - log.debug("wallet created") - } - - @AfterClass - @JvmStatic - fun free_wallet() { - libJna.free_wallet_result(wallet_result) - log.debug("wallet freed") + @Test + fun walletResultError() { + val badWalletResult = WalletResult("bad", "bad", "bad") + assertTrue(badWalletResult.isErr()) + val walletErr = badWalletResult.err() + assertNotNull(walletErr) + log.debug("wallet error $walletErr") + assertEquals("Descriptor", walletErr) + val wallet = badWalletResult.ok() + assertNull(wallet) + } + + @Test + fun walletResultFinalize() { + val names = listOf("one", "two", "three") + names.map { + val wallet = WalletResult(it, desc, change) + assertNotNull(wallet) } + System.gc() + // The only way to verify wallets freed is by checking the log } @Test - fun wallet_sync_error() { - val bad_wallet_result = libJna.new_wallet_result("test", "bad", null) - log.debug("wallet result created") - val sync_result = libJna.sync_wallet(bad_wallet_result) - val sync_err_pointer = libJna.get_void_err(sync_result) - assertNotNull(sync_err_pointer) - val sync_err = sync_err_pointer.getString(0) - log.debug("wallet sync error $sync_err") + fun walletSync() { + val walletResult = WalletResult(name, desc, change) + val wallet = walletResult.ok() + assertNotNull(wallet) + val syncResult = wallet!!.sync(); + assertFalse(syncResult.isErr()) + assertNull(syncResult.err()) } @Test - fun sync() { - val sync_result = libJna.sync_wallet(wallet_result) - assertNull(libJna.get_void_err(sync_result)) - libJna.free_void_result(sync_result) - } - - @Test - fun new_newaddress_wallet() { - val address_result = libJna.new_address(wallet_result) - assertNull(libJna.get_string_err(address_result)) - val address_pointer = libJna.get_string_ok(address_result); - val address = address_pointer!!.getString(0) - - libJna.free_string_result(address_result) - libJna.free_string(address_pointer) + fun walletNewAddress() { + val walletResult = WalletResult(name, desc, change) + val wallet = walletResult.ok() + assertNotNull(wallet) + val addressResult = wallet!!.getAddress() + assertFalse(addressResult.isErr()) + assertNull(addressResult.err()) + val address = addressResult.ok() + assertNotNull(address) log.debug("address created from kotlin: $address") assertEquals(address, "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e") } diff --git a/cc/bdk_ffi_test.c b/cc/bdk_ffi_test.c index e28e5bc..cd01a7b 100644 --- a/cc/bdk_ffi_test.c +++ b/cc/bdk_ffi_test.c @@ -10,10 +10,13 @@ int main (int argc, char const * const argv[]) { WalletResult_t *wallet_result = new_wallet_result("bad", "bad", NULL); assert(wallet_result != NULL); - char *wallet_error = get_wallet_err(wallet_result); - assert(wallet_error != NULL); - //printf("wallet error: %s\n", wallet_error); - free_string(wallet_error); + char *wallet_err = get_wallet_err(wallet_result); + assert(wallet_err != NULL); + assert( 0 == strcmp(wallet_err,"Descriptor") ); + //printf("wallet err: %s\n", wallet_err); + WalletRef_t *wallet_ref = get_wallet_ok(wallet_result); + assert(wallet_ref == NULL); + free_string(wallet_err); free_wallet_result(wallet_result); } @@ -25,26 +28,32 @@ int main (int argc, char const * const argv[]) WalletResult_t *wallet_result = new_wallet_result(name, desc, change); assert(wallet_result != NULL); + char *wallet_err = get_wallet_err(wallet_result); + assert(wallet_err == NULL); + WalletRef_t *wallet_ref = get_wallet_ok(wallet_result); + assert(wallet_ref != NULL); // test sync_wallet - VoidResult_t *sync_result = sync_wallet(wallet_result); + VoidResult_t *sync_result = sync_wallet(wallet_ref); free_void_result(sync_result); // test new_address - StringResult_t *address_result1 = new_address(wallet_result); + StringResult_t *address_result1 = new_address(wallet_ref); char *address1 = get_string_ok(address_result1); //printf("address1: %s\n", address1); assert( 0 == strcmp(address1,"tb1qgkhp034fyxeta00h0nne9tzfm0vsxq4prduzxp")); free_string(address1); free_string_result(address_result1); - StringResult_t *address_result2 = new_address(wallet_result); + StringResult_t *address_result2 = new_address(wallet_ref); char *address2 = get_string_ok(address_result2); //printf("address2: %s\n", address2); assert(0 == strcmp(address2,"tb1qd6u9q327sru2ljvwzdtfrdg36sapax7udz97wf")); free_string(address2); free_string_result(address_result2); + free_wallet_ref(wallet_ref); + // test free_wallet free_wallet_result(wallet_result); diff --git a/src/error.rs b/src/error.rs index fba774c..3aa6d82 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,6 @@ -//use ::safer_ffi::prelude::*; use bdk::Error; -pub fn error_name(error: &bdk::Error) -> &'static str { +pub fn get_name(error: &bdk::Error) -> String { match error { Error::InvalidU32Bytes(_) => "InvalidU32Bytes", Error::Generic(_) => "Generic", @@ -39,159 +38,9 @@ pub fn error_name(error: &bdk::Error) -> &'static str { Error::Hex(_) => "Hex", Error::Psbt(_) => "Psbt", Error::Electrum(_) => "Electrum", -// Error::Esplora(_) => {} -// Error::CompactFilters(_) => {} + // Error::Esplora(_) => {} + // Error::CompactFilters(_) => {} Error::Sled(_) => "Sled", - _ => "Unknown", } + .to_string() } - -//// Errors that can be thrown by the [`Wallet`](crate::wallet::Wallet) -//// Simplified to work over the FFI interface -//#[derive_ReprC] -//#[repr(i16)] -//#[derive(Debug)] -//pub enum WalletError { -// None, -// InvalidU32Bytes, -// Generic, -// ScriptDoesntHaveAddressForm, -// SingleRecipientMultipleOutputs, -// SingleRecipientNoInputs, -// NoRecipients, -// NoUtxosSelected, -// OutputBelowDustLimit, -// InsufficientFunds, -// BnBTotalTriesExceeded, -// BnBNoExactMatch, -// UnknownUtxo, -// TransactionNotFound, -// TransactionConfirmed, -// IrreplaceableTransaction, -// FeeRateTooLow, -// FeeTooLow, -// FeeRateUnavailable, -// MissingKeyOrigin, -// Key, -// ChecksumMismatch, -// SpendingPolicyRequired, -// InvalidPolicyPathError, -// Signer, -// InvalidNetwork, -// InvalidProgressValue, -// ProgressUpdateError, -// InvalidOutpoint, -// Descriptor, -// AddressValidator, -// Encode, -// Miniscript, -// Bip32, -// Secp256k1, -// Json, -// Hex, -// Psbt, -// PsbtParse, -// //#[cfg(feature = "electrum")] -// Electrum, -// //#[cfg(feature = "esplora")] -// //Esplora, -// //#[cfg(feature = "compact_filters")] -// //CompactFilters, -// //#[cfg(feature = "key-value-db")] -// Sled, -//} - -//impl From for WalletError { -// fn from(error: bdk::Error) -> Self { -// match error { -// Error::InvalidU32Bytes(_) => WalletError::InvalidNetwork, -// Error::Generic(_) => WalletError::Generic, -// Error::ScriptDoesntHaveAddressForm => WalletError::ScriptDoesntHaveAddressForm, -// Error::SingleRecipientMultipleOutputs => WalletError::SingleRecipientMultipleOutputs, -// Error::SingleRecipientNoInputs => WalletError::SingleRecipientNoInputs, -// Error::NoRecipients => WalletError::NoRecipients, -// Error::NoUtxosSelected => WalletError::NoUtxosSelected, -// Error::OutputBelowDustLimit(_) => WalletError::OutputBelowDustLimit, -// Error::InsufficientFunds { .. } => WalletError::InsufficientFunds, -// Error::BnBTotalTriesExceeded => WalletError::BnBTotalTriesExceeded, -// Error::BnBNoExactMatch => WalletError::BnBNoExactMatch, -// Error::UnknownUtxo => WalletError::UnknownUtxo, -// Error::TransactionNotFound => WalletError::TransactionNotFound, -// Error::TransactionConfirmed => WalletError::TransactionConfirmed, -// Error::IrreplaceableTransaction => WalletError::IrreplaceableTransaction, -// Error::FeeRateTooLow { .. } => WalletError::FeeRateTooLow, -// Error::FeeTooLow { .. } => WalletError::FeeTooLow, -// Error::MissingKeyOrigin(_) => WalletError::MissingKeyOrigin, -// Error::Key(_) => WalletError::Key, -// Error::ChecksumMismatch => WalletError::ChecksumMismatch, -// Error::SpendingPolicyRequired(_) => WalletError::SpendingPolicyRequired, -// Error::InvalidPolicyPathError(_) => WalletError::InvalidPolicyPathError, -// Error::Signer(_) => WalletError::Signer, -// Error::InvalidProgressValue(_) => WalletError::InvalidProgressValue, -// Error::ProgressUpdateError => WalletError::ProgressUpdateError, -// Error::InvalidOutpoint(_) => WalletError::InvalidOutpoint, -// Error::Descriptor(_) => WalletError::Descriptor, -// Error::AddressValidator(_) => WalletError::AddressValidator, -// Error::Encode(_) => WalletError::Encode, -// Error::Miniscript(_) => WalletError::Miniscript, -// Error::Bip32(_) => WalletError::Bip32, -// Error::Secp256k1(_) => WalletError::Secp256k1, -// Error::Json(_) => WalletError::Json, -// Error::Hex(_) => WalletError::Hex, -// Error::Psbt(_) => WalletError::Psbt, -// Error::Electrum(_) => WalletError::Electrum, -// //Error::Esplora(_) => WalletError::Esplora, -// //Error::CompactFilters(_) => {} -// Error::Sled(_) => WalletError::Sled, -// } -// } -//} - -//type error_code = i16; -// -//impl From for error_code { -// fn from(error: bdk::Error) -> Self { -// match error { -// Error::InvalidU32Bytes(_) => 1, -// Error::Generic(_) => 2, -// Error::ScriptDoesntHaveAddressForm => 3, -// Error::SingleRecipientMultipleOutputs => 4, -// Error::SingleRecipientNoInputs => 5, -// Error::NoRecipients => 6, -// Error::NoUtxosSelected => 7, -// Error::OutputBelowDustLimit(_) => 8, -// Error::InsufficientFunds { .. } => 9, -// Error::BnBTotalTriesExceeded => 10, -// Error::BnBNoExactMatch => 11, -// Error::UnknownUtxo => 12, -// Error::TransactionNotFound => 13, -// Error::TransactionConfirmed => 14, -// Error::IrreplaceableTransaction => 15, -// Error::FeeRateTooLow { .. } => 16, -// Error::FeeTooLow { .. } => 17, -// Error::MissingKeyOrigin(_) => 18, -// Error::Key(_) => 19, -// Error::ChecksumMismatch => 20, -// Error::SpendingPolicyRequired(_) => 21, -// Error::InvalidPolicyPathError(_) => 22, -// Error::Signer(_) => 23, -// Error::InvalidProgressValue(_) => 24, -// Error::ProgressUpdateError => 25, -// Error::InvalidOutpoint(_) => 26, -// Error::Descriptor(_) => 27, -// Error::AddressValidator(_) => 28, -// Error::Encode(_) => 29, -// Error::Miniscript(_) => 30, -// Error::Bip32(_) => 31, -// Error::Secp256k1(_) => 32, -// Error::Json(_) => 33, -// Error::Hex(_) => 34, -// Error::Psbt(_) => 35, -// Error::Electrum(_) => 36, -// //Error::Esplora(_) => WalletError::Esplora, -// //Error::CompactFilters(_) => {} -// Error::Sled(_) => 37, -// _ => -1 -// } -// } -//} diff --git a/src/lib.rs b/src/lib.rs index f3f6fc6..3c75153 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![deny(unsafe_code)] /* No `unsafe` needed! */ +mod error; mod wallet; /// The following test function is necessary for the header generation. @@ -7,6 +8,7 @@ mod wallet; #[test] fn generate_headers() -> ::std::io::Result<()> { ::safer_ffi::headers::builder() + .with_guard("__RUST_BDK_FFI__") .to_file("cc/bdk_ffi.h")? .generate() } diff --git a/src/wallet.rs b/src/wallet.rs index 87b1fcc..cfd72bc 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,3 +1,4 @@ +use crate::error::get_name; use ::safer_ffi::prelude::*; use bdk::bitcoin::network::constants::Network::Testnet; use bdk::blockchain::{ @@ -13,7 +14,7 @@ use safer_ffi::char_p::{char_p_boxed, char_p_ref}; #[derive_ReprC] #[ReprC::opaque] pub struct VoidResult { - raw: Result<(), String>, + raw: Result<(), Error>, } #[ffi_export] @@ -22,7 +23,7 @@ fn get_void_err(void_result: &VoidResult) -> Option { .raw .as_ref() .err() - .map(|s| char_p_boxed::try_from(s.clone()).unwrap()) + .map(|e| char_p_boxed::try_from(get_name(e)).unwrap()) } #[ffi_export] @@ -33,7 +34,7 @@ fn free_void_result(void_result: Option>) { #[derive_ReprC] #[ReprC::opaque] pub struct StringResult { - raw: Result, + raw: Result, } #[ffi_export] @@ -51,7 +52,7 @@ fn get_string_err(string_result: &StringResult) -> Option { .raw .as_ref() .err() - .map(|s| char_p_boxed::try_from(s.clone()).unwrap()) + .map(|e| char_p_boxed::try_from(get_name(e)).unwrap()) } #[ffi_export] @@ -59,10 +60,21 @@ fn free_string_result(string_result: Option>) { drop(string_result) } +#[derive_ReprC] +#[ReprC::opaque] +pub struct WalletRef<'lt> { + raw: &'lt Wallet, +} + +#[ffi_export] +fn free_wallet_ref(wallet_ref: Option>) { + drop(wallet_ref) +} + #[derive_ReprC] #[ReprC::opaque] pub struct WalletResult { - raw: Result, String>, + raw: Result, Error>, } #[ffi_export] @@ -74,12 +86,12 @@ fn new_wallet_result( let name = name.to_string(); let descriptor = descriptor.to_string(); let change_descriptor = change_descriptor.map(|s| s.to_string()); - let wallet_result = new_wallet(name, descriptor, change_descriptor).map_err(|e| e.to_string()); + let wallet_result = new_wallet(name, descriptor, change_descriptor); Box::new(WalletResult { raw: wallet_result }) } fn new_wallet( - name: String, + _name: String, descriptor: String, change_descriptor: Option, ) -> Result, Error> { @@ -108,24 +120,28 @@ fn get_wallet_err(wallet_result: &WalletResult) -> Option { .raw .as_ref() .err() - .map(|s| char_p_boxed::try_from(s.clone()).unwrap()) + .map(|e| char_p_boxed::try_from(get_name(&e)).unwrap()) } #[ffi_export] -fn sync_wallet(wallet_result: &WalletResult) -> Box { - let wallet_result_ref = wallet_result.raw.as_ref().map_err(|error| error.clone()); - let void_result = wallet_result_ref - .and_then(|w| w.sync(log_progress(), Some(100)).map_err(|e| e.to_string())); +fn get_wallet_ok<'lt>(wallet_result: &'lt WalletResult) -> Option>> { + wallet_result + .raw + .as_ref() + .ok() + .map(|w| Box::new(WalletRef { raw: w})) +} + +#[ffi_export] +fn sync_wallet<'lt>(wallet_ref: &'lt WalletRef<'lt>) -> Box { + let void_result = wallet_ref + .raw.sync(log_progress(), Some(100)); Box::new(VoidResult { raw: void_result }) } #[ffi_export] -fn new_address(wallet_result: &WalletResult) -> Box { - let new_address = wallet_result - .raw - .as_ref() - .map_err(|error| error.clone()) - .and_then(|w| w.get_address(New).map_err(|error| error.to_string())); +fn new_address<'lt>(wallet_ref: &'lt WalletRef<'lt>) -> Box { + let new_address = wallet_ref.raw.get_address(New); let string_result = new_address.map(|a| a.to_string()); Box::new(StringResult { raw: string_result }) }