Simplify Kotlin Wallet api

This commit is contained in:
Steve Myers 2021-06-24 13:49:25 -07:00
parent 110dfbd13c
commit 757113c002
15 changed files with 125 additions and 117 deletions

View File

@ -1,9 +1,6 @@
package org.bitcoindevkit.bdk
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.sun.jna.Native
import org.junit.*
import org.junit.runner.RunWith
@ -16,5 +13,5 @@ import org.junit.Assert.*
*/
@RunWith(AndroidJUnit4::class)
class AndroidLibTest : LibTest() {
}

View File

@ -17,8 +17,7 @@ dependencies {
api "org.slf4j:slf4j-api:1.7.30"
testImplementation "ch.qos.logback:logback-classic:1.2.3"
testImplementation "ch.qos.logback:logback-core:1.2.3"
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
testImplementation (project(':test-fixtures'))
testImplementation(project(':test-fixtures'))
}
publishing {

View File

@ -1,6 +1,6 @@
package org.bitcoindevkit.bdk
enum class Error {
enum class JnaError {
InvalidU32Bytes,
Generic,
ScriptDoesntHaveAddressForm,
@ -37,7 +37,8 @@ enum class Error {
Hex,
Psbt,
Electrum,
// Esplora
// Esplora
// CompactFilters
Sled,
}

View File

@ -0,0 +1,3 @@
package org.bitcoindevkit.bdk
class JnaException internal constructor(val err: JnaError) : Exception()

View File

@ -3,7 +3,7 @@ package org.bitcoindevkit.bdk
import com.sun.jna.Native
abstract class LibBase {
protected val libJna: LibJna
get() = Native.load("bdk_ffi", LibJna::class.java)
}

View File

@ -1,6 +1,8 @@
package org.bitcoindevkit.bdk
import com.sun.jna.*
import com.sun.jna.Library
import com.sun.jna.Pointer
import com.sun.jna.PointerType
interface LibJna : Library {
@ -35,13 +37,13 @@ interface LibJna : Library {
// void free_string_result (
// StringResult_t * string_result);
fun free_string_result(string_result: StringResult_t)
// 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)
@ -61,11 +63,11 @@ interface LibJna : Library {
descriptor: String,
changeDescriptor: String?
): WalletResult_t
// char * get_wallet_err (
// WalletResult_t const * wallet_result);
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?

View File

@ -0,0 +1,37 @@
package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
import com.sun.jna.PointerType
import org.slf4j.Logger
import org.slf4j.LoggerFactory
abstract class ResultBase<PT : PointerType, RT : Any> internal constructor(protected val resultT: PT) :
LibBase() {
protected open val log: Logger = LoggerFactory.getLogger(ResultBase::class.java)
protected abstract fun err(): Pointer?
protected abstract fun ok(): RT
protected abstract fun free(pointer: PT)
private fun checkErr() {
val errPointer = err()
val err = errPointer?.getString(0)
libJna.free_string(errPointer)
if (err != null) {
throw JnaException(JnaError.valueOf(err))
}
}
fun value(): RT {
checkErr()
return ok()
}
protected fun finalize() {
free(resultT)
log.debug("$resultT freed")
}
}

View File

@ -1,32 +1,26 @@
package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class StringResult internal constructor(private val stringResultT: LibJna.StringResult_t) : LibBase() {
class StringResult internal constructor(stringResultT: LibJna.StringResult_t) :
ResultBase<LibJna.StringResult_t, String>(stringResultT) {
private val log: Logger = LoggerFactory.getLogger(StringResult::class.java)
override val log: Logger = LoggerFactory.getLogger(StringResult::class.java)
fun isErr(): Boolean {
return libJna.get_string_err(stringResultT) != null
override fun err(): Pointer? {
return libJna.get_string_err(resultT)
}
fun err(): Error? {
val errPointer = libJna.get_string_err(stringResultT)
val err = errPointer?.getString(0)
libJna.free_string(errPointer)
return err?.let { Error.valueOf(it) }
}
fun ok(): String? {
val okPointer = libJna.get_string_ok(stringResultT)
val ok = okPointer?.getString(0)
override fun ok(): String {
val okPointer = libJna.get_string_ok(resultT)
val ok = okPointer!!.getString(0)
libJna.free_string(okPointer)
return ok
}
protected fun finalize() {
libJna.free_string_result(stringResultT)
log.debug("StringResult_t freed")
override fun free(pointer: LibJna.StringResult_t) {
libJna.free_string_result(resultT)
}
}

View File

@ -1,25 +1,23 @@
package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class VoidResult internal constructor(private val voidResultT: LibJna.VoidResult_t) : LibBase() {
class VoidResult internal constructor(voidResultT: LibJna.VoidResult_t) :
ResultBase<LibJna.VoidResult_t, Unit>(voidResultT) {
private val log: Logger = LoggerFactory.getLogger(VoidResult::class.java)
override val log: Logger = LoggerFactory.getLogger(VoidResult::class.java)
fun isErr(): Boolean {
return libJna.get_void_err(voidResultT) != null
override fun err(): Pointer? {
return libJna.get_void_err(resultT)
}
fun err(): Error? {
val errPointer = libJna.get_void_err(voidResultT)
val err = errPointer?.getString(0)
libJna.free_string(errPointer)
return err?.let { Error.valueOf(it) }
override fun ok() {
// Void
}
protected fun finalize() {
libJna.free_void_result(voidResultT)
log.debug("VoidResult_t freed")
override fun free(pointer: LibJna.VoidResult_t) {
libJna.free_void_result(resultT)
}
}

View File

@ -3,23 +3,30 @@ package org.bitcoindevkit.bdk
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class Wallet internal constructor(
private val walletRefT: LibJna.WalletRef_t,
class Wallet constructor(
name: String,
descriptor: String,
changeDescriptor: String?,
) : 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))
val log: Logger = LoggerFactory.getLogger(Wallet::class.java)
private val walletResult =
WalletResult(libJna.new_wallet_result(name, descriptor, changeDescriptor))
private val walletRefT = walletResult.value()
fun sync() {
val voidResult = VoidResult(libJna.sync_wallet(walletRefT.pointer))
return voidResult.value()
}
protected fun finalize() {
fun getAddress(): String {
val stringResult = StringResult(libJna.new_address(walletRefT.pointer))
return stringResult.value()
}
protected fun finalize(pointer: LibJna.WalletResult_t) {
libJna.free_wallet_ref(walletRefT)
log.debug("WalletRef_t freed")
log.debug("$walletRefT freed")
}
}

View File

@ -1,36 +1,23 @@
package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
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
class WalletResult internal constructor(walletResultT: LibJna.WalletResult_t) :
ResultBase<LibJna.WalletResult_t, LibJna.WalletRef_t>(walletResultT) {
override val log: Logger = LoggerFactory.getLogger(WalletResult::class.java)
override fun err(): Pointer? {
return libJna.get_wallet_err(resultT)
}
fun err(): Error? {
val errPointer = libJna.get_wallet_err(walletResultT)
val err = errPointer?.getString(0)
libJna.free_string(errPointer)
return err?.let { Error.valueOf(it) }
override fun ok(): LibJna.WalletRef_t {
return libJna.get_wallet_ok(resultT)!!
}
fun ok(): Wallet? {
val okWalletRef = libJna.get_wallet_ok(walletResultT)
return if (okWalletRef != null) Wallet(okWalletRef) else null
override fun free(pointer: LibJna.WalletResult_t) {
libJna.free_wallet_result(resultT)
}
protected fun finalize() {
libJna.free_wallet_result(walletResultT)
log.debug("WalletResult_t freed")
}
}

View File

@ -1,15 +1,9 @@
package org.bitcoindevkit.bdk
import com.sun.jna.Native
import org.junit.*
import org.junit.Assert.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
/**
* Library test, which will execute on linux host.
*
*/
class JvmLibTest : LibTest() {
}

View File

@ -8,7 +8,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "net.java.dev.jna:jna:5.8.0"
implementation (project(':jvm'))
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
implementation "junit:junit:4.13.2"
//implementation "org.mockito.kotlin:mockito-kotlin:3.2.0"
api "org.slf4j:slf4j-api:1.7.30"
}

View File

@ -4,6 +4,7 @@ import org.junit.*
import org.junit.Assert.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.management.Descriptor
/**
* Library test, which will execute on linux host.
@ -21,21 +22,17 @@ abstract class LibTest : LibBase() {
@Test
fun walletResultError() {
val badWalletResult = WalletResult("bad", "bad", "bad")
assertTrue(badWalletResult.isErr())
val walletErr = badWalletResult.err()
assertNotNull(walletErr)
log.debug("wallet error $walletErr")
assertEquals(Error.Descriptor, walletErr)
val wallet = badWalletResult.ok()
assertNull(wallet)
val jnaException = assertThrows(JnaException::class.java) {
Wallet("bad", "bad", "bad")
}
assertEquals(jnaException.err, JnaError.Descriptor)
}
@Test
fun walletResultFinalize() {
val names = listOf("one", "two", "three")
names.map {
val wallet = WalletResult(it, desc, change)
val wallet = Wallet(it, desc, change)
assertNotNull(wallet)
}
System.gc()
@ -44,23 +41,16 @@ abstract class LibTest : LibBase() {
@Test
fun walletSync() {
val walletResult = WalletResult(name, desc, change)
val wallet = walletResult.ok()
val wallet = Wallet(name, desc, change)
assertNotNull(wallet)
val syncResult = wallet!!.sync();
assertFalse(syncResult.isErr())
assertNull(syncResult.err())
wallet.sync()
}
@Test
fun walletNewAddress() {
val walletResult = WalletResult(name, desc, change)
val wallet = walletResult.ok()
val wallet = Wallet(name, desc, change)
assertNotNull(wallet)
val addressResult = wallet!!.getAddress()
assertFalse(addressResult.isErr())
assertNull(addressResult.err())
val address = addressResult.ok()
val address = wallet.getAddress()
assertNotNull(address)
log.debug("address created from kotlin: $address")
assertEquals(address, "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")

View File

@ -129,13 +129,12 @@ fn get_wallet_ok<'lt>(wallet_result: &'lt WalletResult) -> Option<Box<WalletRef<
.raw
.as_ref()
.ok()
.map(|w| Box::new(WalletRef { raw: w}))
.map(|w| Box::new(WalletRef { raw: w }))
}
#[ffi_export]
fn sync_wallet<'lt>(wallet_ref: &'lt WalletRef<'lt>) -> Box<VoidResult> {
let void_result = wallet_ref
.raw.sync(log_progress(), Some(100));
let void_result = wallet_ref.raw.sync(log_progress(), Some(100));
Box::new(VoidResult { raw: void_result })
}