diff --git a/.gitignore b/.gitignore index 30226da..9d55bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ Cargo.lock /local.properties .gradle wallet_db +bdk_ffi_test diff --git a/bdk_ffi_test.c b/bdk_ffi_test.c new file mode 100644 index 0000000..7c2d492 --- /dev/null +++ b/bdk_ffi_test.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include "bdk_ffi.h" + +int main (int argc, char const * const argv[]) +{ + // test print_string + print_string("hello 123"); + + // test concat_string + char const * string1 = "string1"; + char const * string2 = "string2"; + char * string3 = concat_string(string1, string2); + print_string(string3); + free_string(string3); + // verify free_string after free_string fails + ////free_string(string3); + + // test print_config with c created config + Config_t config1 = { .name = "test", .count = 101 }; + print_config(&config1); + + // test new_config + Config_t * config2 = new_config("test test", 202); + print_config(config2); + + // test free_config + free_config(config2); + // verify print_config after free_config fails (invalid data) + ////print_config(config2); + // verify free_config after free_config fails (double free detected, core dumped) + ////free_config(config2); + + char const * name = "test_wallet"; + char const * desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; + char const * change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; + //char const * change = NULL; + + // test new_wallet + { + WalletPtr_t * wallet = new_wallet(name, desc, change); + + // test sync_wallet + sync_wallet(wallet); + printf("after sync_wallet\n"); + sync_wallet(wallet); + printf("after sync_wallet\n"); + + // test new_address + char * address1 = new_address(wallet); + printf("address1: %s\n", address1); + free_string(address1); + assert(address1 != NULL); + char * address2 = new_address(wallet); + printf("address2: %s\n", address2); + assert(address2 != NULL); + free_string(address2); + + // test free_wallet + free_wallet(wallet); + printf("after free_wallet\n"); + + // test free_wallet NULL doesn't crash + free_wallet(NULL); + + // verify sync_wallet after sync_wallet fails (double free detected, core dumped) + ////sync_wallet(&wallet); + } + + return EXIT_SUCCESS; +} diff --git a/build.sh b/build.sh index d80153a..5e9eab6 100755 --- a/build.sh +++ b/build.sh @@ -1,9 +1,13 @@ +# rust cargo build cargo test --features c-headers -- generate_headers -cc main.c -o main -L target/debug -l bdk_ffi -l pthread -l dl -l m -./main +export LD_LIBRARY_PATH=`pwd`/target/debug + +# cc +cc bdk_ffi_test.c -o bdk_ffi_test -L target/debug -l bdk_ffi -l pthread -l dl -l m +#valgrind --leak-check=full ./bdk_ffi_test +./bdk_ffi_test # jvm mkdir -p jvm/build/jniLibs/x86_64_linux cp target/debug/libbdk_ffi.so jvm/build/jniLibs/x86_64_linux -export LD_LIBRARY_PATH=`pwd`/target/debug diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..62d4c05 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..622ab64 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/jvm/build.gradle b/jvm/build.gradle index c9ab9a8..f06e84c 100644 --- a/jvm/build.gradle +++ b/jvm/build.gradle @@ -7,9 +7,9 @@ plugins { test { systemProperty "java.library.path", file("${buildDir}/jniLibs/x86_64_linux").absolutePath environment "LD_LIBRARY_PATH", file("${buildDir}/jniLibs/x86_64_linux").absolutePath -// testLogging { -// events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR" -// } + testLogging { + events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR" + } } task buildRust(type: Exec) { diff --git a/jvm/src/main/java/org/bitcoindevkit/bdkjni/Lib.kt b/jvm/src/main/java/org/bitcoindevkit/bdkjni/Lib.kt index d1b7456..033a42f 100644 --- a/jvm/src/main/java/org/bitcoindevkit/bdkjni/Lib.kt +++ b/jvm/src/main/java/org/bitcoindevkit/bdkjni/Lib.kt @@ -10,18 +10,19 @@ import com.sun.jna.ptr.PointerByReference // int32_t count; // // } Config_t; -//@Structure.FieldOrder("x", "y") +@Structure.FieldOrder("name", "count") class Config_t : Structure() { @JvmField var name: String? = null @JvmField var count: NativeLong? = null - - override fun getFieldOrder() = listOf("name", "count") } // typedef struct WalletPtr WalletPtr_t; -//class WalletPtr_t : PointerType() +class WalletPtr_t : PointerType { + constructor(): super() + constructor(pointer: Pointer): super(pointer) +} interface Lib : Library { @@ -38,10 +39,6 @@ interface Lib : Library { // char * string); fun free_string(string: String) - // void print_int ( - // int64_t number); - fun print_int(number: Int) - // void print_config ( // Config_t const * config); fun print_config(config: Config_t) @@ -59,18 +56,18 @@ interface Lib : Library { // char const * name, // char const * descriptor, // char const * change_descriptor); - //fun new_wallet(name: String, descriptor: String, changeDescriptor: String?): WalletPtr_t + fun new_wallet(name: String, descriptor: String, changeDescriptor: String?): WalletPtr_t // void sync_wallet ( // WalletPtr_t * const * wallet); //fun sync_wallet(wallet: WalletPtr_t) - //fun sync_wallet(wallet: WalletPtr_t) + fun sync_wallet(wallet: WalletPtr_t) // char * new_address ( // WalletPtr_t * const * wallet); - //fun new_address(wallet: WalletPtr_t): String + fun new_address(wallet: WalletPtr_t): String // void free_wallet ( // WalletPtr_t * wallet); - //fun free_wallet(wallet: WalletPtr_t) + fun free_wallet(wallet: WalletPtr_t) } diff --git a/jvm/src/test/java/org/bitcoindevkit/bdkjni/LibTest.kt b/jvm/src/test/java/org/bitcoindevkit/bdkjni/LibTest.kt index 6d935fe..3c32f94 100644 --- a/jvm/src/test/java/org/bitcoindevkit/bdkjni/LibTest.kt +++ b/jvm/src/test/java/org/bitcoindevkit/bdkjni/LibTest.kt @@ -2,8 +2,6 @@ package org.bitcoindevkit.bdkjni import com.sun.jna.Native import com.sun.jna.NativeLong -import com.sun.jna.Pointer -import com.sun.jna.ptr.PointerByReference import org.junit.Test /** @@ -43,26 +41,26 @@ class LibTest { lib.free_config(config) } -// @Test -// fun new_sync_free_wallet() { -// val name = "test_wallet" -// val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" -// val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" -// -// val wallet = lib.new_wallet(name, desc, change) -// println("wallet created in kotlin: $wallet") -// lib.sync_wallet(wallet) -// //lib.free_wallet(wallet) -// } + @Test + fun new_sync_free_wallet() { + val name = "test_wallet" + val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" + val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" -// @Test -// fun new_newaddress_wallet() { -// val name = "test_wallet" -// val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" -// val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" -// -// val config = lib.new_config("test test", NativeLong(Long.MAX_VALUE)) -// lib.print_config(config) -// lib.free_config(config) -// } + val wallet = lib.new_wallet(name, desc, change) + lib.sync_wallet(wallet) + lib.free_wallet(wallet) + } + + @Test + fun new_newaddress_wallet() { + val name = "test_wallet" + val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" + val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" + + val wallet = lib.new_wallet(name, desc, change) + val address = lib.new_address(wallet) + println("address created from kotlin: $address") + lib.free_wallet(wallet) + } } diff --git a/main.c b/main.c deleted file mode 100644 index 6d133e4..0000000 --- a/main.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include "bdk_ffi.h" - -int main (int argc, char const * const argv[]) -{ - // test print_string - print_string("hello 123"); - - // test concat_string - char const * string1 = "string1"; - char const * string2 = "string2"; - char * string3 = concat_string(string1, string2); - print_string(string3); - free_string(string3); - // verify free_string after free_string fails - ////free_string(string3); - - // test print_config with c created config - Config_t config1 = { .name = "test", .count = 101 }; - print_config(&config1); - - // test new_config - Config_t * config2 = new_config("test test", 202); - print_config(config2); - - // test free_config - free_config(config2); - // verify print_config after free_config fails (invalid data) - ////print_config(config2); - // verify free_config after free_config fails (double free detected, core dumped) - ////free_config(config2); - - //char const * name = "test_wallet"; - //char const * desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; - //char const * change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; - - ////printf("wallet name: %s\n", name); - ////printf("descriptor: %s\n", desc); - ////printf("change descriptor: %s\n", change); - //WalletPtr_t * wallet = new_wallet(name, desc, change); - - //sync_wallet(&wallet); - //sync_wallet(&wallet); - - //char const * address1 = new_address(&wallet); - //printf("address1: %s\n", address1); - //char const * address2 = new_address(&wallet); - //printf("address: %s\n", address2); - - //free_wallet(wallet); - ////sync_wallet(&wallet); - - return EXIT_SUCCESS; -} diff --git a/src/lib.rs b/src/lib.rs index 714fe9f..7776e47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,73 +64,68 @@ fn free_config(config: Box) { drop(config) } -//#[derive_ReprC] -//#[ReprC::opaque] -//pub struct WalletPtr { -// raw: Wallet, -//} +#[derive_ReprC] +#[ReprC::opaque] +pub struct WalletPtr { + raw: Wallet, +} -//impl From> for WalletPtr { -// fn from(wallet: Wallet) -> Self { -// WalletPtr { -// raw: wallet, -// } -// } -//} +impl From> for WalletPtr { + fn from(wallet: Wallet) -> Self { + WalletPtr { + raw: wallet, + } + } +} -//#[ffi_export] -//fn new_wallet<'a>( -// name: char_p_ref<'a>, -// descriptor: char_p_ref<'a>, -// change_descriptor: Option>, -//) -> Box { -// let name = name.to_string(); -// let descriptor = descriptor.to_string(); -// let change_descriptor = change_descriptor.map(|s| s.to_string()); -// -// let database = sled::open("./wallet_db").unwrap(); -// let tree = database.open_tree(name.clone()).unwrap(); -// -// let descriptor: &str = descriptor.as_str(); -// let change_descriptor: Option<&str> = change_descriptor.as_deref(); -// -// let electrum_url = "ssl://electrum.blockstream.info:60002"; -// let client = Client::new(&electrum_url).unwrap(); -// -// let wallet = Wallet::new( -// descriptor, -// change_descriptor, -// Testnet, -// tree, -// ElectrumBlockchain::from(client), -// ) -// .unwrap(); -// -// Box::new(WalletPtr::from(wallet)) -//} +#[ffi_export] +fn new_wallet( + name: char_p_ref, + descriptor: char_p_ref, + change_descriptor: Option, +) -> Box { + let name = name.to_string(); + let descriptor = descriptor.to_string(); + let change_descriptor = change_descriptor.map(|s| s.to_string()); -//#[ffi_export] -//fn sync_wallet( wallet: &Box) { -// println!("before sync"); -// let _r = wallet.raw.sync(log_progress(), Some(100)); -// println!("after sync"); -//} + let database = sled::open("./wallet_db").unwrap(); + let tree = database.open_tree(name.clone()).unwrap(); -//#[ffi_export] -//fn new_address( wallet: &Box) -> char_p_boxed { -// println!("before new_address"); -// let new_address = wallet.raw.get_address(New); -// println!("after new_address: {:?}", new_address); -// let new_address = new_address.unwrap(); -// let new_address = new_address.to_string(); -// println!("new address: ${}", new_address); -// new_address.try_into().unwrap() -//} + let descriptor: &str = descriptor.as_str(); + let change_descriptor: Option<&str> = change_descriptor.as_deref(); -//#[ffi_export] -//fn free_wallet( wallet: Box) { -// drop(wallet) -//} + let electrum_url = "ssl://electrum.blockstream.info:60002"; + let client = Client::new(&electrum_url).unwrap(); + + let wallet = Wallet::new( + descriptor, + change_descriptor, + Testnet, + tree, + ElectrumBlockchain::from(client), + ) + .unwrap(); + println!("created wallet"); + Box::new(WalletPtr::from(wallet)) +} + +#[ffi_export] +fn sync_wallet( wallet: &WalletPtr) { + let _r = wallet.raw.sync(log_progress(), Some(100)); +} + +#[ffi_export] +fn new_address( wallet: &WalletPtr) -> char_p_boxed { + let new_address = wallet.raw.get_address(New); + let new_address = new_address.unwrap(); + let new_address = new_address.to_string(); + new_address.try_into().unwrap() +} + +#[ffi_export] +fn free_wallet( wallet: Option>) { + drop(wallet) +} /// The following test function is necessary for the header generation. #[::safer_ffi::cfg_headers]