Fix kotlin wallet struct access via JNA opaque pointer

This commit is contained in:
Steve Myers 2021-06-10 13:40:58 -07:00
parent 8deb39ac76
commit a5ad4cd0a5
10 changed files with 175 additions and 158 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ Cargo.lock
/local.properties /local.properties
.gradle .gradle
wallet_db wallet_db
bdk_ffi_test

72
bdk_ffi_test.c Normal file
View File

@ -0,0 +1,72 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#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;
}

View File

@ -1,9 +1,13 @@
# rust
cargo build cargo build
cargo test --features c-headers -- generate_headers cargo test --features c-headers -- generate_headers
cc main.c -o main -L target/debug -l bdk_ffi -l pthread -l dl -l m export LD_LIBRARY_PATH=`pwd`/target/debug
./main
# 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 # jvm
mkdir -p jvm/build/jniLibs/x86_64_linux mkdir -p jvm/build/jniLibs/x86_64_linux
cp target/debug/libbdk_ffi.so 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

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -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

View File

@ -7,9 +7,9 @@ plugins {
test { test {
systemProperty "java.library.path", file("${buildDir}/jniLibs/x86_64_linux").absolutePath systemProperty "java.library.path", file("${buildDir}/jniLibs/x86_64_linux").absolutePath
environment "LD_LIBRARY_PATH", file("${buildDir}/jniLibs/x86_64_linux").absolutePath environment "LD_LIBRARY_PATH", file("${buildDir}/jniLibs/x86_64_linux").absolutePath
// testLogging { testLogging {
// events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR" events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR"
// } }
} }
task buildRust(type: Exec) { task buildRust(type: Exec) {

View File

@ -10,18 +10,19 @@ import com.sun.jna.ptr.PointerByReference
// int32_t count; // int32_t count;
// //
// } Config_t; // } Config_t;
//@Structure.FieldOrder("x", "y") @Structure.FieldOrder("name", "count")
class Config_t : Structure() { class Config_t : Structure() {
@JvmField @JvmField
var name: String? = null var name: String? = null
@JvmField @JvmField
var count: NativeLong? = null var count: NativeLong? = null
override fun getFieldOrder() = listOf("name", "count")
} }
// typedef struct WalletPtr WalletPtr_t; // typedef struct WalletPtr WalletPtr_t;
//class WalletPtr_t : PointerType() class WalletPtr_t : PointerType {
constructor(): super()
constructor(pointer: Pointer): super(pointer)
}
interface Lib : Library { interface Lib : Library {
@ -38,10 +39,6 @@ interface Lib : Library {
// char * string); // char * string);
fun free_string(string: String) fun free_string(string: String)
// void print_int (
// int64_t number);
fun print_int(number: Int)
// void print_config ( // void print_config (
// Config_t const * config); // Config_t const * config);
fun print_config(config: Config_t) fun print_config(config: Config_t)
@ -59,18 +56,18 @@ interface Lib : Library {
// char const * name, // char const * name,
// char const * descriptor, // char const * descriptor,
// char const * change_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 ( // void sync_wallet (
// WalletPtr_t * const * wallet); // WalletPtr_t * const * wallet);
//fun sync_wallet(wallet: WalletPtr_t) //fun sync_wallet(wallet: WalletPtr_t)
//fun sync_wallet(wallet: WalletPtr_t) fun sync_wallet(wallet: WalletPtr_t)
// char * new_address ( // char * new_address (
// WalletPtr_t * const * wallet); // WalletPtr_t * const * wallet);
//fun new_address(wallet: WalletPtr_t): String fun new_address(wallet: WalletPtr_t): String
// void free_wallet ( // void free_wallet (
// WalletPtr_t * wallet); // WalletPtr_t * wallet);
//fun free_wallet(wallet: WalletPtr_t) fun free_wallet(wallet: WalletPtr_t)
} }

View File

@ -2,8 +2,6 @@ package org.bitcoindevkit.bdkjni
import com.sun.jna.Native import com.sun.jna.Native
import com.sun.jna.NativeLong import com.sun.jna.NativeLong
import com.sun.jna.Pointer
import com.sun.jna.ptr.PointerByReference
import org.junit.Test import org.junit.Test
/** /**
@ -43,26 +41,26 @@ class LibTest {
lib.free_config(config) lib.free_config(config)
} }
// @Test @Test
// fun new_sync_free_wallet() { fun new_sync_free_wallet() {
// val name = "test_wallet" val name = "test_wallet"
// val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
// val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" 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 val wallet = lib.new_wallet(name, desc, change)
// fun new_newaddress_wallet() { lib.sync_wallet(wallet)
// val name = "test_wallet" lib.free_wallet(wallet)
// val desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" }
// val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
// @Test
// val config = lib.new_config("test test", NativeLong(Long.MAX_VALUE)) fun new_newaddress_wallet() {
// lib.print_config(config) val name = "test_wallet"
// lib.free_config(config) 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)
}
} }

55
main.c
View File

@ -1,55 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#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;
}

View File

@ -64,73 +64,68 @@ fn free_config(config: Box<Config>) {
drop(config) drop(config)
} }
//#[derive_ReprC] #[derive_ReprC]
//#[ReprC::opaque] #[ReprC::opaque]
//pub struct WalletPtr { pub struct WalletPtr {
// raw: Wallet<ElectrumBlockchain, Tree>, raw: Wallet<ElectrumBlockchain, Tree>,
//} }
//impl From<Wallet<ElectrumBlockchain, Tree>> for WalletPtr { impl From<Wallet<ElectrumBlockchain, Tree>> for WalletPtr {
// fn from(wallet: Wallet<ElectrumBlockchain, Tree>) -> Self { fn from(wallet: Wallet<ElectrumBlockchain, Tree>) -> Self {
// WalletPtr { WalletPtr {
// raw: wallet, raw: wallet,
// } }
// } }
//} }
//#[ffi_export] #[ffi_export]
//fn new_wallet<'a>( fn new_wallet(
// name: char_p_ref<'a>, name: char_p_ref,
// descriptor: char_p_ref<'a>, descriptor: char_p_ref,
// change_descriptor: Option<char_p_ref<'a>>, change_descriptor: Option<char_p_ref>,
//) -> Box<WalletPtr> { ) -> Box<WalletPtr> {
// let name = name.to_string(); let name = name.to_string();
// let descriptor = descriptor.to_string(); let descriptor = descriptor.to_string();
// let change_descriptor = change_descriptor.map(|s| s.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] let database = sled::open("./wallet_db").unwrap();
//fn sync_wallet( wallet: &Box<WalletPtr>) { let tree = database.open_tree(name.clone()).unwrap();
// println!("before sync");
// let _r = wallet.raw.sync(log_progress(), Some(100));
// println!("after sync");
//}
//#[ffi_export] let descriptor: &str = descriptor.as_str();
//fn new_address( wallet: &Box<WalletPtr>) -> char_p_boxed { let change_descriptor: Option<&str> = change_descriptor.as_deref();
// 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()
//}
//#[ffi_export] let electrum_url = "ssl://electrum.blockstream.info:60002";
//fn free_wallet( wallet: Box<WalletPtr>) { let client = Client::new(&electrum_url).unwrap();
// drop(wallet)
//} 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<Box<WalletPtr>>) {
drop(wallet)
}
/// The following test function is necessary for the header generation. /// The following test function is necessary for the header generation.
#[::safer_ffi::cfg_headers] #[::safer_ffi::cfg_headers]