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 package org.bitcoindevkit.bdk
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.sun.jna.Native
import org.junit.*
import org.junit.runner.RunWith import org.junit.runner.RunWith

View File

@ -17,8 +17,7 @@ dependencies {
api "org.slf4j:slf4j-api:1.7.30" api "org.slf4j:slf4j-api:1.7.30"
testImplementation "ch.qos.logback:logback-classic:1.2.3" testImplementation "ch.qos.logback:logback-classic:1.2.3"
testImplementation "ch.qos.logback:logback-core: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 { publishing {

View File

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

View File

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

View File

@ -1,6 +1,8 @@
package org.bitcoindevkit.bdk 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 { interface LibJna : Library {

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 package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory 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 { override fun err(): Pointer? {
return libJna.get_string_err(stringResultT) != null return libJna.get_string_err(resultT)
} }
fun err(): Error? { override fun ok(): String {
val errPointer = libJna.get_string_err(stringResultT) val okPointer = libJna.get_string_ok(resultT)
val err = errPointer?.getString(0) val ok = okPointer!!.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)
libJna.free_string(okPointer) libJna.free_string(okPointer)
return ok return ok
} }
protected fun finalize() { override fun free(pointer: LibJna.StringResult_t) {
libJna.free_string_result(stringResultT) libJna.free_string_result(resultT)
log.debug("StringResult_t freed")
} }
} }

View File

@ -1,25 +1,23 @@
package org.bitcoindevkit.bdk package org.bitcoindevkit.bdk
import com.sun.jna.Pointer
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory 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 { override fun err(): Pointer? {
return libJna.get_void_err(voidResultT) != null return libJna.get_void_err(resultT)
} }
fun err(): Error? { override fun ok() {
val errPointer = libJna.get_void_err(voidResultT) // Void
val err = errPointer?.getString(0)
libJna.free_string(errPointer)
return err?.let { Error.valueOf(it) }
} }
protected fun finalize() { override fun free(pointer: LibJna.VoidResult_t) {
libJna.free_void_result(voidResultT) libJna.free_void_result(resultT)
log.debug("VoidResult_t freed")
} }
} }

View File

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

View File

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

View File

@ -1,11 +1,5 @@
package org.bitcoindevkit.bdk 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. * Library test, which will execute on linux host.
* *

View File

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

View File

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

View File

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