From cd813a14b1ce5a1e2e83a940242762bfba10a78a Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Sun, 4 Jul 2021 15:54:23 -0700 Subject: [PATCH] Add Wallet.balance() --- .../kotlin/org/bitcoindevkit/bdk/LibJna.kt | 33 ++++++- .../bitcoindevkit/bdk/types/UInt64Result.kt | 31 ++++++ .../bdk/wallet/VecLocalUtxoResult.kt | 2 +- .../org/bitcoindevkit/bdk/wallet/Wallet.kt | 6 ++ .../kotlin/org/bitcoindevkit/bdk/LibTest.kt | 9 ++ cc/bdk_ffi.h | 98 +++++++++++-------- cc/bdk_ffi_test.c | 19 ++-- src/types.rs | 5 + src/wallet/mod.rs | 20 +++- 9 files changed, 167 insertions(+), 56 deletions(-) create mode 100644 bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/types/UInt64Result.kt diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt index ffc36db..98b5a19 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt @@ -232,10 +232,35 @@ interface LibJna : Library { override fun getFieldOrder() = listOf("ok", "err") } - // void free_unspent_result ( + // void free_veclocalutxo_result ( // FfiResult_Vec_LocalUtxo_t unspent_result); - fun free_unspent_result(unspent_result: FfiResultVec_LocalUtxo_t.ByValue) + fun free_veclocalutxo_result(unspent_result: FfiResultVec_LocalUtxo_t.ByValue) + // typedef struct { + // + // uint64_t ok; + // + // FfiError_t err; + // + // } FfiResult_uint64_t; + open class FfiResult_uint64_t : Structure() { + + class ByValue : FfiResult_uint64_t(), Structure.ByValue + class ByReference : FfiResult_uint64_t(), Structure.ByReference + + @JvmField + var ok: Long = Long.MIN_VALUE + + @JvmField + var err: Short = 0 + + override fun getFieldOrder() = listOf("ok", "err") + } + + // void free_uint64_result ( + // FfiResult_uint64_t void_result); + fun free_uint64_result(unspent_result: FfiResult_uint64_t.ByValue) + // FfiResultVoid_t sync_wallet ( // OpaqueWallet_t const * opaque_wallet); fun sync_wallet(opaque_wallet: OpaqueWallet_t): FfiResultVoid_t.ByValue @@ -248,6 +273,10 @@ interface LibJna : Library { // OpaqueWallet_t const * opaque_wallet); fun list_unspent(opaque_wallet: OpaqueWallet_t): FfiResultVec_LocalUtxo_t.ByValue + // FfiResult_uint64_t balance ( + // OpaqueWallet_t const * opaque_wallet); + fun balance(opaque_wallet: OpaqueWallet_t): FfiResult_uint64_t.ByValue + // DatabaseConfig_t * new_memory_config (void); fun new_memory_config(): DatabaseConfig_t diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/types/UInt64Result.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/types/UInt64Result.kt new file mode 100644 index 0000000..43a7eb7 --- /dev/null +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/types/UInt64Result.kt @@ -0,0 +1,31 @@ +package org.bitcoindevkit.bdk.types + +import org.bitcoindevkit.bdk.FfiException +import org.bitcoindevkit.bdk.LibBase +import org.bitcoindevkit.bdk.LibJna +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class UInt64Result constructor(private val ffiResultUint64T: LibJna.FfiResult_uint64_t.ByValue) : + LibBase() { + + private val log: Logger = LoggerFactory.getLogger(UInt64Result::class.java) + + fun value(): Long { + val err = ffiResultUint64T.err + val ok = ffiResultUint64T.ok + when { + err > 0 -> { + throw FfiException(err) + } + else -> { + return ok + } + } + } + + protected fun finalize() { + libJna.free_uint64_result(ffiResultUint64T) + log.debug("$ffiResultUint64T freed") + } +} \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/VecLocalUtxoResult.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/VecLocalUtxoResult.kt index c569c49..4f48d45 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/VecLocalUtxoResult.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/VecLocalUtxoResult.kt @@ -26,7 +26,7 @@ class VecLocalUtxoResult(private val ffiResultVecLocalUtxoT: LibJna.FfiResultVec } protected fun finalize() { - libJna.free_unspent_result(ffiResultVecLocalUtxoT) + libJna.free_veclocalutxo_result(ffiResultVecLocalUtxoT) log.debug("$ffiResultVecLocalUtxoT freed") } } \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt index c8d9e4d..940dfda 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt @@ -5,6 +5,7 @@ import org.bitcoindevkit.bdk.DatabaseConfig import org.bitcoindevkit.bdk.LibBase import org.bitcoindevkit.bdk.LibJna import org.bitcoindevkit.bdk.types.StringResult +import org.bitcoindevkit.bdk.types.UInt64Result import org.bitcoindevkit.bdk.types.VoidResult import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -42,4 +43,9 @@ class Wallet constructor( val vecLocalUtxoResult = VecLocalUtxoResult(libJna.list_unspent(wallet)) return vecLocalUtxoResult.value() } + + fun balance(): Long { + val longResult = UInt64Result(libJna.balance(wallet)) + return longResult.value() + } } \ No newline at end of file diff --git a/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt b/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt index 79d99d0..2ecfa69 100644 --- a/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt +++ b/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt @@ -96,4 +96,13 @@ abstract class LibTest : LibBase() { assertTrue(it.keychain!! >= 0) } } + + @Test + fun walletBalance() { + val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + wallet.sync() + val balance = wallet.balance() + //log.debug("balance from kotlin: $balance") + assertTrue(balance > 0) + } } diff --git a/cc/bdk_ffi.h b/cc/bdk_ffi.h index 3832f3e..89f0ea2 100644 --- a/cc/bdk_ffi.h +++ b/cc/bdk_ffi.h @@ -14,6 +14,12 @@ extern "C" { #endif +typedef struct BlockchainConfig BlockchainConfig_t; + +typedef struct DatabaseConfig DatabaseConfig_t; + +typedef struct OpaqueWallet OpaqueWallet_t; + #include #include @@ -107,47 +113,6 @@ FfiError_t #endif ; -typedef struct { - - char * ok; - - FfiError_t err; - -} FfiResult_char_ptr_t; - -void free_string_result ( - FfiResult_char_ptr_t string_result); - -typedef struct { - - FfiError_t err; - -} FfiResultVoid_t; - -void free_void_result ( - FfiResultVoid_t void_result); - -/** \brief - * Free a Rust-allocated string - */ -void free_string ( - char * string); - -typedef struct BlockchainConfig BlockchainConfig_t; - -BlockchainConfig_t * new_electrum_config ( - char const * url, - char const * socks5, - int16_t retry, - int16_t timeout); - -void free_blockchain_config ( - BlockchainConfig_t * blockchain_config); - -typedef struct DatabaseConfig DatabaseConfig_t; - -typedef struct OpaqueWallet OpaqueWallet_t; - typedef struct { OpaqueWallet_t * ok; @@ -165,9 +130,23 @@ FfiResult_OpaqueWallet_ptr_t new_wallet_result ( void free_wallet_result ( FfiResult_OpaqueWallet_ptr_t wallet_result); +typedef struct { + + FfiError_t err; + +} FfiResultVoid_t; + FfiResultVoid_t sync_wallet ( OpaqueWallet_t const * opaque_wallet); +typedef struct { + + char * ok; + + FfiError_t err; + +} FfiResult_char_ptr_t; + FfiResult_char_ptr_t new_address ( OpaqueWallet_t const * opaque_wallet); @@ -221,9 +200,44 @@ typedef struct { FfiResult_Vec_LocalUtxo_t list_unspent ( OpaqueWallet_t const * opaque_wallet); -void free_unspent_result ( +void free_veclocalutxo_result ( FfiResult_Vec_LocalUtxo_t unspent_result); +typedef struct { + + uint64_t ok; + + FfiError_t err; + +} FfiResult_uint64_t; + +FfiResult_uint64_t balance ( + OpaqueWallet_t const * opaque_wallet); + +BlockchainConfig_t * new_electrum_config ( + char const * url, + char const * socks5, + int16_t retry, + int16_t timeout); + +void free_blockchain_config ( + BlockchainConfig_t * blockchain_config); + +void free_string_result ( + FfiResult_char_ptr_t string_result); + +void free_void_result ( + FfiResultVoid_t void_result); + +void free_uint64_result ( + FfiResult_uint64_t void_result); + +/** \brief + * Free a Rust-allocated string + */ +void free_string ( + char * string); + DatabaseConfig_t * new_memory_config (void); DatabaseConfig_t * new_sled_config ( diff --git a/cc/bdk_ffi_test.c b/cc/bdk_ffi_test.c index fb6674a..82d9417 100644 --- a/cc/bdk_ffi_test.c +++ b/cc/bdk_ffi_test.c @@ -114,12 +114,12 @@ int main (int argc, char const * const argv[]) assert(unspent_ptr[i].keychain >= 0); } - free_unspent_result(unspent_result); + free_veclocalutxo_result(unspent_result); free_wallet_result(wallet_result); } // test balance - /*{ + { char const *desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; char const *change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; @@ -128,7 +128,7 @@ int main (int argc, char const * const argv[]) // new wallet FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config); - assert(wallet_result.err == NULL); + assert(wallet_result.err == FFI_ERROR_NONE); assert(wallet_result.ok != NULL); free_blockchain_config(bc_config); @@ -138,19 +138,20 @@ int main (int argc, char const * const argv[]) // sync wallet FfiResultVoid_t sync_result = sync_wallet(wallet); - assert(sync_result.err == NULL); + assert(sync_result.err == FFI_ERROR_NONE); free_void_result(sync_result); // get balance - FfiResultT_uint64_t balance_result = balance(wallet); - assert(balance_result.err == NULL); - printf("balance = %lu\n", balance_result.ok); + FfiResult_uint64_t balance_result = balance(wallet); + //printf("balance.err = %d\n", (balance_result.err)); + assert(balance_result.err == FFI_ERROR_NONE); + //printf("balance.ok = %ld\n", balance_result.ok); assert(balance_result.ok > 0); // free balance and wallet results - free_u64_result(balance_result); + free_uint64_result(balance_result); free_wallet_result(wallet_result); - }*/ + } return EXIT_SUCCESS; } diff --git a/src/types.rs b/src/types.rs index 5d856d6..241cb42 100644 --- a/src/types.rs +++ b/src/types.rs @@ -27,6 +27,11 @@ fn free_void_result(void_result: FfiResultVoid) { drop(void_result) } +#[ffi_export] +fn free_uint64_result(void_result: FfiResult) { + drop(void_result) +} + // TODO do we need this? remove? /// Free a Rust-allocated string #[ffi_export] diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 017a0ed..c395b23 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1,4 +1,5 @@ use std::convert::TryFrom; +use std::ffi::CString; use ::safer_ffi::prelude::*; use bdk::bitcoin::network::constants::Network::Testnet; @@ -14,7 +15,6 @@ use database::DatabaseConfig; use crate::error::FfiError; use crate::types::{FfiResult, FfiResultVoid}; -use std::ffi::CString; mod blockchain; mod database; @@ -125,10 +125,26 @@ fn list_unspent(opaque_wallet: &OpaqueWallet) -> FfiResult>) { +fn free_veclocalutxo_result(unspent_result: FfiResult>) { drop(unspent_result) } +#[ffi_export] +fn balance(opaque_wallet: &OpaqueWallet) -> FfiResult { + let balance_result = opaque_wallet.raw.get_balance(); + + match balance_result { + Ok(b) => FfiResult { + ok: b, + err: FfiError::None, + }, + Err(e) => FfiResult { + ok: u64::MIN, + err: FfiError::from(&e), + }, + } +} + // Non-opaque returned values #[derive_ReprC]