[WIP] reorganize and remove old stuff
This commit is contained in:
		
							parent
							
								
									a7f7ab0ef9
								
							
						
					
					
						commit
						290db0105f
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -7,4 +7,4 @@ wallet_db | ||||
| bdk_ffi_test | ||||
| local.properties | ||||
| *.log | ||||
| targets/kotlin/testdb | ||||
| *.dylib | ||||
| @ -1,7 +1,7 @@ | ||||
| [package] | ||||
| name = "uniffi_bdk" | ||||
| version = "0.1.0" | ||||
| authors = ["Steve Myers <steve@notmandatory.org>"] | ||||
| authors = ["Steve Myers <steve@notmandatory.org>", ""] | ||||
| edition = "2018" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| @ -3,9 +3,9 @@ UniFFI | ||||
| 
 | ||||
| 1. cargo install uniffi_bindgen  | ||||
| 2. cargo build | ||||
| 3. uniffi-bindgen generate --no-format --out-dir targets/kotlin/src/main/kotlin src/bdk.udl --language kotlin | ||||
| 4. cp target/debug/libuniffi_bdk.dylib targets/kotlin/src/main/resources/darwin-x86-64 | ||||
| 5. gradle build -Djna.debug_load=true -Djna.debug_load.jna | ||||
| 3. uniffi-bindgen generate --no-format --out-dir bindings/bdk-kotlin/src/main/kotlin src/bdk.udl --language kotlin | ||||
| 4. cp target/debug/libuniffi_bdk.dylib bindings/bdk-kotlin/src/main/resources/darwin-x86-64 | ||||
| 5. cd bindings/bdk-kotlin; gradle build -Djna.debug_load=true -Djna.debug_load.jna | ||||
| 
 | ||||
| 
 | ||||
| Setup Android build environment | ||||
|  | ||||
							
								
								
									
										7
									
								
								bdk-kotlin/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								bdk-kotlin/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,7 +0,0 @@ | ||||
| /target | ||||
| .idea | ||||
| .gradle | ||||
| local.properties | ||||
| build | ||||
| *.so | ||||
| *.dylib | ||||
| @ -1,71 +0,0 @@ | ||||
| apply plugin: 'com.android.library' | ||||
| apply plugin: 'kotlin-android' | ||||
| apply plugin: 'maven-publish' | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 30 | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 30 | ||||
|         versionCode 1 | ||||
|         versionName "1.0" | ||||
| 
 | ||||
|         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||
|         consumerProguardFiles 'consumer-rules.pro' | ||||
|     } | ||||
| 
 | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled false | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| afterEvaluate { | ||||
| 
 | ||||
|     publishing { | ||||
|         publications { | ||||
|             // Creates a Maven publication called "release". | ||||
|             release(MavenPublication) { | ||||
|                 // Applies the component for the release build variant. | ||||
|                 from components.release | ||||
| 
 | ||||
|                 // You can then customize attributes of the publication as shown below. | ||||
|                 groupId = 'org.bitcoindevkit' | ||||
|                 artifactId = 'bdk' | ||||
|                 version = '0.0.1-SNAPSHOT' | ||||
|             } | ||||
|             // Creates a Maven publication called “debug”. | ||||
|             debug(MavenPublication) { | ||||
|                 // Applies the component for the debug build variant. | ||||
|                 from components.debug | ||||
| 
 | ||||
|                 groupId = 'org.bitcoindevkit' | ||||
|                 artifactId = 'bdk-debug' | ||||
|                 version = '0.0.1-SNAPSHOT' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation(project(':jvm')) { | ||||
|         exclude group: 'net.java.dev.jna', module: 'jna' | ||||
|     } | ||||
| 
 | ||||
|     implementation 'net.java.dev.jna:jna:5.8.0@aar' | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||||
|     implementation 'androidx.appcompat:appcompat:1.3.0' | ||||
|     implementation 'androidx.core:core-ktx:1.5.0' | ||||
|     api "org.slf4j:slf4j-api:1.7.30" | ||||
| 
 | ||||
|     androidTestImplementation 'com.github.tony19:logback-android:2.0.0' | ||||
|     androidTestImplementation(project(':test-fixtures')) { | ||||
|         exclude group: 'net.java.dev.jna', module: 'jna' | ||||
|     } | ||||
|     androidTestImplementation 'androidx.test.ext:junit:1.1.2' | ||||
|     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' | ||||
|     androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1' | ||||
| } | ||||
							
								
								
									
										26
									
								
								bdk-kotlin/android/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								bdk-kotlin/android/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @ -1,26 +0,0 @@ | ||||
| # Add project specific ProGuard rules here. | ||||
| # You can control the set of applied configuration files using the | ||||
| # proguardFiles setting in build.gradle. | ||||
| # | ||||
| # For more details, see | ||||
| #   http://developer.android.com/guide/developing/tools/proguard.html | ||||
| 
 | ||||
| # If your project uses WebView with JS, uncomment the following | ||||
| # and specify the fully qualified class name to the JavaScript interface | ||||
| # class: | ||||
| #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||
| #   public *; | ||||
| #} | ||||
| 
 | ||||
| # Uncomment this to preserve the line number information for | ||||
| # debugging stack traces. | ||||
| #-keepattributes SourceFile,LineNumberTable | ||||
| 
 | ||||
| # If you keep the line number information, uncomment this to | ||||
| # hide the original source file name. | ||||
| #-renamesourcefileattribute SourceFile | ||||
| 
 | ||||
| # for JNA | ||||
| -dontwarn java.awt.* | ||||
| -keep class com.sun.jna.* { *; } | ||||
| -keepclassmembers class * extends com.sun.jna.* { public *; } | ||||
| @ -1,14 +0,0 @@ | ||||
| <configuration> | ||||
|     <appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender"> | ||||
|         <tagEncoder> | ||||
|             <pattern>%logger{12}</pattern> | ||||
|         </tagEncoder> | ||||
|         <encoder> | ||||
|             <pattern>[%-20thread] %msg</pattern> | ||||
|         </encoder> | ||||
|     </appender> | ||||
| 
 | ||||
|     <root level="DEBUG"> | ||||
|         <appender-ref ref="logcat" /> | ||||
|     </root> | ||||
| </configuration> | ||||
| @ -1,21 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import android.app.Application | ||||
| import android.content.Context.MODE_PRIVATE | ||||
| import androidx.test.core.app.ApplicationProvider | ||||
| import androidx.test.ext.junit.runners.AndroidJUnit4 | ||||
| import org.junit.runner.RunWith | ||||
| 
 | ||||
| /** | ||||
|  * Instrumented test, which will execute on an Android device. | ||||
|  * | ||||
|  * See [testing documentation](http://d.android.com/tools/testing). | ||||
|  */ | ||||
| @RunWith(AndroidJUnit4::class) | ||||
| class AndroidLibTest : LibTest() { | ||||
|     override fun getTestDataDir(): String { | ||||
|         val context = ApplicationProvider.getApplicationContext<Application>() | ||||
|         return context.getDir("bdk-test", MODE_PRIVATE).toString() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,6 +0,0 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="org.bitcoindevkit.bdk"> | ||||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
| 
 | ||||
| </manifest> | ||||
| @ -1,22 +0,0 @@ | ||||
| buildscript { | ||||
|     ext.kotlin_version = '1.5.10' | ||||
|     repositories { | ||||
|         google() | ||||
|         mavenCentral() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:4.2.1' | ||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| allprojects { | ||||
|     repositories { | ||||
|         google() | ||||
|         mavenCentral() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| task clean(type: Delete) { | ||||
|     delete rootProject.buildDir | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| plugins { | ||||
|     id 'org.jetbrains.kotlin.jvm' | ||||
|     id 'java-library' | ||||
|     id 'maven-publish' | ||||
| } | ||||
| 
 | ||||
| test { | ||||
|     testLogging { | ||||
|         events "PASSED", "SKIPPED", "FAILED", "STANDARD_OUT", "STANDARD_ERROR" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation platform('org.jetbrains.kotlin:kotlin-bom') | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||||
|     implementation "net.java.dev.jna:jna:5.8.0" | ||||
|     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(project(':test-fixtures')) | ||||
| } | ||||
| 
 | ||||
| publishing { | ||||
|     publications { | ||||
|         maven(MavenPublication) { | ||||
|             groupId = 'org.bitcoindevkit' | ||||
|             artifactId = 'bdk' | ||||
|             version = '0.0.1-SNAPSHOT' | ||||
| 
 | ||||
|             from components.java | ||||
|         } | ||||
|     } | ||||
| } | ||||
| java { | ||||
|     sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|     targetCompatibility = JavaVersion.VERSION_1_8 | ||||
| } | ||||
| @ -1,45 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| enum class FfiError { | ||||
|     None, | ||||
|     InvalidU32Bytes, | ||||
|     Generic, | ||||
|     ScriptDoesntHaveAddressForm, | ||||
|     SingleRecipientMultipleOutputs, | ||||
|     SingleRecipientNoInputs, | ||||
|     NoRecipients, | ||||
|     NoUtxosSelected, | ||||
|     OutputBelowDustLimit, | ||||
|     InsufficientFunds, | ||||
|     BnBTotalTriesExceeded, | ||||
|     BnBNoExactMatch, | ||||
|     UnknownUtxo, | ||||
|     TransactionNotFound, | ||||
|     TransactionConfirmed, | ||||
|     IrreplaceableTransaction, | ||||
|     FeeRateTooLow, | ||||
|     FeeTooLow, | ||||
|     MissingKeyOrigin, | ||||
|     Key, | ||||
|     ChecksumMismatch, | ||||
|     SpendingPolicyRequired, | ||||
|     InvalidPolicyPathError, | ||||
|     Signer, | ||||
|     InvalidProgressValue, | ||||
|     ProgressUpdateError, | ||||
|     InvalidOutpoint, | ||||
|     Descriptor, | ||||
|     AddressValidator, | ||||
|     Encode, | ||||
|     Miniscript, | ||||
|     Bip32, | ||||
|     Secp256k1, | ||||
|     Json, | ||||
|     Hex, | ||||
|     Psbt, | ||||
|     Electrum, | ||||
| 
 | ||||
|     //        Esplora | ||||
|     //        CompactFilters | ||||
|     Sled, | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class FfiException(val err: FfiError) : Exception() { | ||||
|     private val log: Logger = LoggerFactory.getLogger(FfiException::class.java) | ||||
| 
 | ||||
|     init { | ||||
|         log.error("JnaError: [{}] {}",err.ordinal, err.name) | ||||
|     } | ||||
| 
 | ||||
|     internal constructor(err: Short) : this(FfiError.values()[err.toInt()]) | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import com.sun.jna.Native | ||||
| 
 | ||||
| abstract class LibBase { | ||||
| 
 | ||||
|     protected val libJna: LibJna | ||||
|         get() = Native.load("bdk_ffi", LibJna::class.java) | ||||
| } | ||||
| @ -1,407 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import com.sun.jna.* | ||||
| 
 | ||||
| interface LibJna : Library { | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    char * ok; | ||||
|     // | ||||
|     //    FfiError_t err; | ||||
|     // | ||||
|     //} FfiResult_char_ptr_t; | ||||
|     open class FfiResult_char_ptr_t : Structure() { | ||||
|         class ByValue : FfiResult_char_ptr_t(), Structure.ByValue | ||||
|         class ByReference : FfiResult_char_ptr_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var ok: String? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("ok", "err") | ||||
|     } | ||||
| 
 | ||||
|     // void free_string_result ( | ||||
|     //    FfiResult_char_ptr_t string_result); | ||||
|     fun free_string_result(string_result: FfiResult_char_ptr_t.ByValue) | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    FfiError_t err; | ||||
|     // | ||||
|     //} FfiResultVoid_t; | ||||
|     open class FfiResultVoid_t : Structure() { | ||||
|         class ByValue : FfiResultVoid_t(), Structure.ByValue | ||||
|         class ByReference : FfiResultVoid_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
|          | ||||
|         override fun getFieldOrder() = listOf("err") | ||||
|     } | ||||
| 
 | ||||
|     // void free_void_result ( | ||||
|     //    FfiResultVoid_t void_result); | ||||
|     fun free_void_result(void_result: FfiResultVoid_t.ByValue) | ||||
| 
 | ||||
|     // void free_string ( | ||||
|     //    char * string); | ||||
|     fun free_string(string: Pointer?) | ||||
| 
 | ||||
|     // typedef struct BlockchainConfig BlockchainConfig_t; | ||||
|     class BlockchainConfig_t : PointerType { | ||||
|         constructor() : super() | ||||
|         constructor(pointer: Pointer) : super(pointer) | ||||
|     } | ||||
| 
 | ||||
|     // BlockchainConfig_t * new_electrum_config ( | ||||
|     //    char const * url, | ||||
|     //    char const * socks5, | ||||
|     //    int16_t retry, | ||||
|     //    int16_t timeout, | ||||
|     //    size_t stop_gap); | ||||
|     fun new_electrum_config( | ||||
|         url: String, | ||||
|         socks5: String?, | ||||
|         retry: Short, | ||||
|         timeout: Short, | ||||
|         stop_gap: Long, | ||||
|     ): BlockchainConfig_t | ||||
| 
 | ||||
|     // void free_blockchain_config ( | ||||
|     //    BlockchainConfig_t * blockchain_config); | ||||
|     fun free_blockchain_config(blockchain_config: BlockchainConfig_t) | ||||
| 
 | ||||
|     // typedef struct DatabaseConfig DatabaseConfig_t; | ||||
|     class DatabaseConfig_t : PointerType { | ||||
|         constructor() : super() | ||||
|         constructor(pointer: Pointer) : super(pointer) | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct OpaqueWallet OpaqueWallet_t; | ||||
|     class OpaqueWallet_t : PointerType { | ||||
|         constructor() : super() | ||||
|         constructor(pointer: Pointer) : super(pointer) | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    OpaqueWallet_t * ok; | ||||
|     // | ||||
|     //    FfiError_t err; | ||||
|     // | ||||
|     // } FfiResult_OpaqueWallet_ptr_t; | ||||
|     open class FfiResult_OpaqueWallet_ptr_t : Structure() { | ||||
|         class ByValue : FfiResult_OpaqueWallet_ptr_t(), Structure.ByValue | ||||
|         class ByReference : FfiResult_OpaqueWallet_ptr_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var ok: OpaqueWallet_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
|          | ||||
|         override fun getFieldOrder() = listOf("ok", "err") | ||||
|     } | ||||
| 
 | ||||
|     // FfiResult_OpaqueWallet_ptr_t new_wallet_result ( | ||||
|     //    char const * descriptor, | ||||
|     //    char const * change_descriptor, | ||||
|     //    char const * network, | ||||
|     //    BlockchainConfig_t const * blockchain_config, | ||||
|     //    DatabaseConfig_t const * database_config); | ||||
|     fun new_wallet_result( | ||||
|         descriptor: String, | ||||
|         changeDescriptor: String?, | ||||
|         network: String, | ||||
|         blockchainConfig: BlockchainConfig_t, | ||||
|         databaseConfig: DatabaseConfig_t, | ||||
|     ): FfiResult_OpaqueWallet_ptr_t.ByValue | ||||
| 
 | ||||
|     // void free_wallet_result ( | ||||
|     //    FfiResult_OpaqueWallet_ptr_t wallet_result); | ||||
|     fun free_wallet_result(wallet_result: FfiResult_OpaqueWallet_ptr_t.ByValue) | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    char * txid; | ||||
|     // | ||||
|     //    uint32_t vout; | ||||
|     // | ||||
|     // } OutPoint_t; | ||||
|     open class OutPoint_t : Structure() { | ||||
|         class ByValue : OutPoint_t(), Structure.ByValue | ||||
| 
 | ||||
|         @JvmField | ||||
|         var txid: String? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var vout: Int? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("txid", "vout") | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    uint64_t value; | ||||
|     // | ||||
|     //    char * script_pubkey; | ||||
|     // | ||||
|     // } TxOut_t; | ||||
|     open class TxOut_t : Structure() { | ||||
|         class ByValue : TxOut_t(), Structure.ByValue | ||||
| 
 | ||||
|         @JvmField | ||||
|         var value: Long? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var script_pubkey: String? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("value", "script_pubkey") | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    OutPoint_t outpoint; | ||||
|     // | ||||
|     //    TxOut_t txout; | ||||
|     // | ||||
|     //    uint16_t keychain; | ||||
|     // | ||||
|     // } LocalUtxo_t; | ||||
|     open class LocalUtxo_t : Structure() { | ||||
|          | ||||
|         class ByValue : LocalUtxo_t(), Structure.ByValue | ||||
|         class ByReference : LocalUtxo_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var outpoint: OutPoint_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var txout: TxOut_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var keychain: Short? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("outpoint", "txout", "keychain") | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    LocalUtxo_t * ptr; | ||||
|     // | ||||
|     //    size_t len; | ||||
|     // | ||||
|     //    size_t cap; | ||||
|     // | ||||
|     // } Vec_LocalUtxo_t; | ||||
|     open class Vec_LocalUtxo_t : Structure() { | ||||
|          | ||||
|         class ByReference : Vec_LocalUtxo_t(), Structure.ByReference | ||||
|         class ByValue : Vec_LocalUtxo_t(), Structure.ByValue | ||||
|          | ||||
|         @JvmField | ||||
|         var ptr: LocalUtxo_t.ByReference? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var len: NativeLong? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var cap: NativeLong? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("ptr", "len", "cap") | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    Vec_LocalUtxo_t ok; | ||||
|     // | ||||
|     //    FfiError_t err; | ||||
|     // | ||||
|     // } FfiResult_Vec_LocalUtxo_t; | ||||
|     open class FfiResultVec_LocalUtxo_t : Structure() { | ||||
|          | ||||
|         class ByValue : FfiResultVec_LocalUtxo_t(), Structure.ByValue | ||||
|         class ByReference : FfiResultVec_LocalUtxo_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var ok: Vec_LocalUtxo_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("ok", "err") | ||||
|     } | ||||
| 
 | ||||
|     // void free_veclocalutxo_result ( | ||||
|     //    FfiResult_Vec_LocalUtxo_t unspent_result); | ||||
|     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? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
| 
 | ||||
|         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 | ||||
| 
 | ||||
|     // FfiResult_char_ptr_t new_address ( | ||||
|     //    OpaqueWallet_t const * opaque_wallet); | ||||
|     fun new_address(opaque_wallet: OpaqueWallet_t): FfiResult_char_ptr_t.ByValue | ||||
| 
 | ||||
|     // FfiResult_Vec_LocalUtxo_t list_unspent ( | ||||
|     //    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 | ||||
| 
 | ||||
|     // DatabaseConfig_t * new_sled_config ( | ||||
|     //    char const * path, | ||||
|     //    char const * tree_name); | ||||
|     fun new_sled_config(path: String, tree_name: String): DatabaseConfig_t | ||||
| 
 | ||||
|     // void free_database_config ( | ||||
|     //    DatabaseConfig_t * database_config); | ||||
|     fun free_database_config(database_config: DatabaseConfig_t) | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     //    uint32_t height; | ||||
|     //    uint64_t timestamp; | ||||
|     // } ConfirmationTime_t; | ||||
|     open class ConfirmationTime_t : Structure() { | ||||
|          | ||||
|         class ByValue : ConfirmationTime_t(), Structure.ByValue | ||||
|         class ByReference : ConfirmationTime_t(), Structure.ByReference | ||||
|          | ||||
|         @JvmField | ||||
|         var height: Int? = null | ||||
|          | ||||
|         @JvmField | ||||
|         var timestamp: Long? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("height", "timestamp") | ||||
|     } | ||||
| 
 | ||||
|     // typedef struct { | ||||
|     //    char * txid; | ||||
|     //    uint64_t received; | ||||
|     //    uint64_t sent; | ||||
|     //    int64_t fee; | ||||
|     //    bool is_confirmed; | ||||
|     //    ConfirmationTime_t confirmation_time; | ||||
|     //    bool verified; | ||||
|     // } TransactionDetails_t; | ||||
|     open class TransactionDetails_t : Structure() { | ||||
| 
 | ||||
|         class ByValue : TransactionDetails_t(), Structure.ByValue | ||||
|         class ByReference : TransactionDetails_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var txid: String? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var received: Long? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var sent: Long? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var fee: Long? = null | ||||
|          | ||||
|         @JvmField | ||||
|         var is_confirmed: Boolean? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var confirmation_time: ConfirmationTime_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var verified: Boolean? = null | ||||
|          | ||||
|         override fun getFieldOrder() = listOf("txid", "received", "sent", "fee", "is_confirmed", "confirmation_time", "verified") | ||||
|     } | ||||
|      | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    TransactionDetails_t * ptr; | ||||
|     // | ||||
|     //    size_t len; | ||||
|     // | ||||
|     //    size_t cap; | ||||
|     // | ||||
|     // } Vec_TransactionDetails_t; | ||||
|     open class Vec_TransactionDetails_t : Structure() { | ||||
| 
 | ||||
|         class ByReference : Vec_TransactionDetails_t(), Structure.ByReference | ||||
|         class ByValue : Vec_TransactionDetails_t(), Structure.ByValue | ||||
| 
 | ||||
|         @JvmField | ||||
|         var ptr: TransactionDetails_t.ByReference? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var len: NativeLong? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var cap: NativeLong? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("ptr", "len", "cap") | ||||
|     } | ||||
|      | ||||
|     // typedef struct { | ||||
|     // | ||||
|     //    Vec_TransactionDetails_t ok; | ||||
|     // | ||||
|     //    FfiError_t err; | ||||
|     // | ||||
|     // } FfiResult_Vec_TransactionDetails_t; | ||||
|     open class FfiResult_Vec_TransactionDetails_t : Structure() { | ||||
| 
 | ||||
|         class ByValue : FfiResult_Vec_TransactionDetails_t(), Structure.ByValue | ||||
|         class ByReference : FfiResult_Vec_TransactionDetails_t(), Structure.ByReference | ||||
| 
 | ||||
|         @JvmField | ||||
|         var ok: Vec_TransactionDetails_t? = null | ||||
| 
 | ||||
|         @JvmField | ||||
|         var err: Short? = null | ||||
| 
 | ||||
|         override fun getFieldOrder() = listOf("ok", "err") | ||||
|     } | ||||
|      | ||||
|     // FfiResult_Vec_TransactionDetails_t list_transactions ( | ||||
|     //    OpaqueWallet_t const * opaque_wallet); | ||||
|     fun list_transactions(opaque_wallet: OpaqueWallet_t): FfiResult_Vec_TransactionDetails_t.ByValue | ||||
| 
 | ||||
| 
 | ||||
|     // void free_vectxdetails_result ( | ||||
|     //    FfiResult_Vec_TransactionDetails_t txdetails_result); | ||||
|     fun free_vectxdetails_result(txdetails_result: FfiResult_Vec_TransactionDetails_t.ByValue) | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| 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 StringResult constructor(private val ffiResultCharPtrT: LibJna.FfiResult_char_ptr_t.ByValue) : | ||||
|     LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(StringResult::class.java) | ||||
| 
 | ||||
|     fun value(): String { | ||||
|         val err = ffiResultCharPtrT.err!! | ||||
|         val ok = ffiResultCharPtrT.ok!! | ||||
|         when { | ||||
|             err > 0 -> { | ||||
|                 throw FfiException(err) | ||||
|             } | ||||
|             else -> { | ||||
|                 return ok | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_string_result(ffiResultCharPtrT) | ||||
|         log.debug("$ffiResultCharPtrT freed") | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| 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") | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| 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 VoidResult constructor(private val ffiResultVoidT: LibJna.FfiResultVoid_t.ByValue) : | ||||
|     LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(VoidResult::class.java) | ||||
| 
 | ||||
|     fun value(): Unit { | ||||
|         val err = ffiResultVoidT.err!! | ||||
| 
 | ||||
|         when { | ||||
|             err > 0 -> { | ||||
|                 throw FfiException(err) | ||||
|             } | ||||
|             else -> { | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_void_result(ffiResultVoidT) | ||||
|         log.debug("$ffiResultVoidT freed") | ||||
|     } | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| abstract class BlockchainConfig : LibBase() { | ||||
|     private val log: Logger = LoggerFactory.getLogger(BlockchainConfig::class.java) | ||||
|     abstract val blockchainConfigT: LibJna.BlockchainConfig_t | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_blockchain_config(blockchainConfigT) | ||||
|         log.debug("$blockchainConfigT freed") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class ElectrumConfig( | ||||
|     url: String, | ||||
|     socks5: String?, | ||||
|     retry: Short, | ||||
|     timeout: Short, | ||||
|     stopGap: Long, | ||||
| ) : BlockchainConfig() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(ElectrumConfig::class.java) | ||||
|     override val blockchainConfigT = libJna.new_electrum_config(url, socks5, retry, timeout, stopGap) | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| abstract class DatabaseConfig : LibBase() { | ||||
|     private val log: Logger = LoggerFactory.getLogger(DatabaseConfig::class.java) | ||||
|     abstract val databaseConfigT: LibJna.DatabaseConfig_t | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_database_config(databaseConfigT) | ||||
|         log.debug("$databaseConfigT freed") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class MemoryConfig : DatabaseConfig() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(MemoryConfig::class.java) | ||||
|     override val databaseConfigT = libJna.new_memory_config() | ||||
| } | ||||
| 
 | ||||
| class SledConfig(path: String, treeName: String) : DatabaseConfig() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(SledConfig::class.java) | ||||
|     override val databaseConfigT = libJna.new_sled_config(path, treeName) | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk.wallet | ||||
| 
 | ||||
| import org.bitcoindevkit.bdk.FfiException | ||||
| import org.bitcoindevkit.bdk.LibBase | ||||
| import org.bitcoindevkit.bdk.LibJna | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class VecLocalUtxoResult(private val ffiResultVecLocalUtxoT: LibJna.FfiResultVec_LocalUtxo_t.ByValue) : | ||||
|     LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(VecLocalUtxoResult::class.java) | ||||
| 
 | ||||
|     fun value(): Array<LibJna.LocalUtxo_t.ByReference> { | ||||
|         val err = ffiResultVecLocalUtxoT.err!! | ||||
|         val ok = ffiResultVecLocalUtxoT.ok!! | ||||
|         when { | ||||
|             err > 0 -> { | ||||
|                 throw FfiException(err) | ||||
|             } | ||||
|             else -> { | ||||
|                 val first = ok.ptr!! | ||||
|                 return first.toArray(ok.len!!.toInt()) as Array<LibJna.LocalUtxo_t.ByReference> | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_veclocalutxo_result(ffiResultVecLocalUtxoT) | ||||
|         log.debug("$ffiResultVecLocalUtxoT freed") | ||||
|     } | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk.wallet | ||||
| 
 | ||||
| import org.bitcoindevkit.bdk.FfiException | ||||
| import org.bitcoindevkit.bdk.LibBase | ||||
| import org.bitcoindevkit.bdk.LibJna | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class VecTxDetailsResult(private val ffiResultVecTransactionDetailsT: LibJna.FfiResult_Vec_TransactionDetails_t.ByValue) : | ||||
|     LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(VecTxDetailsResult::class.java) | ||||
| 
 | ||||
|     fun value(): Array<LibJna.TransactionDetails_t.ByReference> { | ||||
|         val err = ffiResultVecTransactionDetailsT.err!! | ||||
|         val ok = ffiResultVecTransactionDetailsT.ok!! | ||||
|         when { | ||||
|             err > 0 -> { | ||||
|                 throw FfiException(err) | ||||
|             } | ||||
|             else -> { | ||||
|                 val first = ok.ptr!! | ||||
|                 return first.toArray(ok.len!!.toInt()) as Array<LibJna.TransactionDetails_t.ByReference> | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_vectxdetails_result(ffiResultVecTransactionDetailsT) | ||||
|         log.debug("$ffiResultVecTransactionDetailsT freed") | ||||
|     } | ||||
| } | ||||
| @ -1,65 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk.wallet | ||||
| 
 | ||||
| import org.bitcoindevkit.bdk.BlockchainConfig | ||||
| 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 | ||||
| 
 | ||||
| enum class Network { | ||||
|     Bitcoin, | ||||
|     Testnet, | ||||
|     Signet, | ||||
|     Regtest, | ||||
| } | ||||
| 
 | ||||
| class Wallet constructor( | ||||
|     descriptor: String, | ||||
|     changeDescriptor: String?, | ||||
|     network: Network, | ||||
|     blockchainConfig: BlockchainConfig, | ||||
|     databaseConfig: DatabaseConfig, | ||||
| ) : LibBase() { | ||||
| 
 | ||||
|     val log: Logger = LoggerFactory.getLogger(Wallet::class.java) | ||||
| 
 | ||||
|     private val walletResult = WalletResult( | ||||
|         libJna.new_wallet_result( | ||||
|             descriptor, | ||||
|             changeDescriptor, | ||||
|             network.toString().lowercase(), | ||||
|             blockchainConfig.blockchainConfigT, | ||||
|             databaseConfig.databaseConfigT | ||||
|         ) | ||||
|     ) | ||||
|     private val wallet = walletResult.value() | ||||
| 
 | ||||
|     fun sync() { | ||||
|         val voidResult = VoidResult(libJna.sync_wallet(wallet)) | ||||
|         return voidResult.value() | ||||
|     } | ||||
| 
 | ||||
|     fun getAddress(): String { | ||||
|         val stringResult = StringResult(libJna.new_address(wallet)) | ||||
|         return stringResult.value() | ||||
|     } | ||||
|      | ||||
|     fun listUnspent(): Array<LibJna.LocalUtxo_t.ByReference> { | ||||
|         val vecLocalUtxoResult = VecLocalUtxoResult(libJna.list_unspent(wallet)) | ||||
|         return vecLocalUtxoResult.value() | ||||
|     } | ||||
|      | ||||
|     fun balance(): Long { | ||||
|         val longResult = UInt64Result(libJna.balance(wallet)) | ||||
|         return longResult.value() | ||||
|     } | ||||
|      | ||||
|     fun listTransactionDetails(): Array<LibJna.TransactionDetails_t.ByReference> { | ||||
|         val vecTxDetailsResult = VecTxDetailsResult(libJna.list_transactions((wallet))) | ||||
|         return vecTxDetailsResult.value() | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk.wallet | ||||
| 
 | ||||
| import org.bitcoindevkit.bdk.FfiException | ||||
| import org.bitcoindevkit.bdk.LibBase | ||||
| import org.bitcoindevkit.bdk.LibJna | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| 
 | ||||
| class WalletResult constructor(private val ffiResultOpaqueWalletPtrT: LibJna.FfiResult_OpaqueWallet_ptr_t.ByValue) : | ||||
|     LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(WalletResult::class.java) | ||||
| 
 | ||||
|     fun value(): LibJna.OpaqueWallet_t { | ||||
|         val err = ffiResultOpaqueWalletPtrT.err!! | ||||
|         val ok = ffiResultOpaqueWalletPtrT.ok | ||||
|         when { | ||||
|             err > 0 -> { | ||||
|                 throw FfiException(err) | ||||
|             } | ||||
|             else -> { | ||||
|                 return ok!! | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun finalize() { | ||||
|         libJna.free_wallet_result(ffiResultOpaqueWalletPtrT) | ||||
|         log.debug("$ffiResultOpaqueWalletPtrT freed") | ||||
|     } | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import java.nio.file.Paths | ||||
| 
 | ||||
| /** | ||||
|  * Library test, which will execute on linux host. | ||||
|  * | ||||
|  */ | ||||
| class JvmLibTest : LibTest() { | ||||
| 
 | ||||
|     override fun getTestDataDir(): String { | ||||
|         //return Files.createTempDirectory("bdk-test").toString() | ||||
|         return Paths.get(System.getProperty("java.io.tmpdir"), "bdk-test").toString() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,3 +0,0 @@ | ||||
| rootProject.name = 'bdk-kotlin' | ||||
| 
 | ||||
| include ':jvm', ':android', ':test-fixtures' | ||||
| @ -1,19 +0,0 @@ | ||||
| plugins { | ||||
|     id 'org.jetbrains.kotlin.jvm' | ||||
|     id 'java-library' | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation platform('org.jetbrains.kotlin:kotlin-bom') | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||||
|     implementation "net.java.dev.jna:jna:5.8.0" | ||||
|     implementation(project(':jvm')) | ||||
|     implementation "junit:junit:4.13.2" | ||||
|     //implementation "org.mockito.kotlin:mockito-kotlin:3.2.0" | ||||
|     api "org.slf4j:slf4j-api:1.7.30" | ||||
| } | ||||
| 
 | ||||
| java { | ||||
|     sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|     targetCompatibility = JavaVersion.VERSION_1_8 | ||||
| } | ||||
| @ -1,135 +0,0 @@ | ||||
| package org.bitcoindevkit.bdk | ||||
| 
 | ||||
| import org.bitcoindevkit.bdk.wallet.Network | ||||
| import org.bitcoindevkit.bdk.wallet.Wallet | ||||
| import org.junit.Assert.* | ||||
| import org.junit.Test | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| import java.io.File | ||||
| 
 | ||||
| /** | ||||
|  * Library tests which will execute for jvm and android modules. | ||||
|  */ | ||||
| abstract class LibTest : LibBase() { | ||||
| 
 | ||||
|     private val log: Logger = LoggerFactory.getLogger(LibTest::class.java) | ||||
| 
 | ||||
|     val name = "test_wallet" | ||||
|     val desc = | ||||
|         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" | ||||
|     val change = | ||||
|         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" | ||||
|     val network = Network.Testnet | ||||
|      | ||||
|     val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30, 100) | ||||
|     val databaseConfig = MemoryConfig() | ||||
| 
 | ||||
|     abstract fun getTestDataDir(): String | ||||
| 
 | ||||
|     fun cleanupTestDataDir() { | ||||
|         File(getTestDataDir()).deleteRecursively() | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletResultError() { | ||||
|         val jnaException = assertThrows(FfiException::class.java) { | ||||
|             Wallet("bad", "bad", network, blockchainConfig, databaseConfig) | ||||
|         } | ||||
|         assertEquals(jnaException.err, FfiError.Descriptor) | ||||
|     } | ||||
| 
 | ||||
| //    @Test | ||||
| //    fun walletResultFinalize() { | ||||
| //        run { | ||||
| //            val desc = | ||||
| //                "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" | ||||
| //            val change = | ||||
| //                "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" | ||||
| // | ||||
| //            val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30) | ||||
| //            val databaseConfig = MemoryConfig() | ||||
| //            val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) | ||||
| //            wallet.sync() | ||||
| //            assertNotNull(wallet.getAddress()) | ||||
| //        } | ||||
| //        System.gc() | ||||
| //        Thread.sleep(2000) | ||||
| //        // The only way to verify wallets freed is by checking the log | ||||
| //    } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletSync() { | ||||
|         val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30, 100) | ||||
|         val testDataDir = getTestDataDir() | ||||
|         // log.debug("testDataDir = $testDataDir") | ||||
|         val databaseConfig = SledConfig(testDataDir, "steve-test") | ||||
|         val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) | ||||
|         wallet.sync() | ||||
|         cleanupTestDataDir() | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletNewAddress() { | ||||
|         val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) | ||||
|         val address = wallet.getAddress() | ||||
|         assertNotNull(address) | ||||
|         // log.debug("address created from kotlin: $address") | ||||
|         assertEquals(address, "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletUnspent() { | ||||
|         val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) | ||||
|         wallet.sync() | ||||
|         val unspent = wallet.listUnspent() | ||||
|         assertTrue(unspent.isNotEmpty()) | ||||
|          | ||||
|         unspent.iterator().forEach { | ||||
|             //log.debug("unspent.outpoint.txid: ${it.outpoint!!.txid}") | ||||
|             assertNotNull(it.outpoint?.txid) | ||||
|             //log.debug("unspent.outpoint.vout: ${it.outpoint?.vout}") | ||||
|             assertTrue(it.outpoint?.vout!! >= 0) | ||||
|             //log.debug("unspent.txout.value: ${it.txout?.value}") | ||||
|             assertTrue(it.txout?.value!! > 0) | ||||
|             //log.debug("unspent.txout.script_pubkey: ${it.txout?.script_pubkey}") | ||||
|             assertNotNull(it.txout?.script_pubkey) | ||||
|             //log.debug("unspent.keychain: ${it.keychain}") | ||||
|             assertTrue(it.keychain!! >= 0) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletBalance() { | ||||
|         val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) | ||||
|         wallet.sync() | ||||
|         val balance = wallet.balance() | ||||
|         //log.debug("balance from kotlin: $balance") | ||||
|         assertTrue(balance > 0) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun walletTxDetails() { | ||||
|         val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) | ||||
|         wallet.sync() | ||||
|         val txDetails = wallet.listTransactionDetails() | ||||
|         assertTrue(txDetails.isNotEmpty()) | ||||
| 
 | ||||
|         txDetails.iterator().forEach { | ||||
|             //log.debug("txDetails.txid: ${it.txid}") | ||||
|             assertNotNull(it.txid) | ||||
|             //log.debug("txDetails.received: ${it.received}") | ||||
|             //log.debug("txDetails.sent: ${it.sent}") | ||||
|             assertTrue(it.received!! > 0 || it.sent!! > 0) | ||||
|             //log.debug("txDetails.fee: ${it.fee}") | ||||
|             assertTrue(it.fee!! > 0) | ||||
|             //log.debug("txDetails.is_confirmed: ${it.is_confirmed}") | ||||
|             assertTrue(it.is_confirmed!!) | ||||
|             assertNotNull(it.confirmation_time!!) | ||||
|             //log.debug("txDetails.confirmation_time.timestamp: ${it.confirmation_time!!.timestamp}") | ||||
|             assertTrue(it.confirmation_time!!.timestamp!! > 0) | ||||
|             //log.debug("txDetails.confirmation_time.height: ${it.confirmation_time!!.height}") | ||||
|             assertTrue(it.confirmation_time!!.height!! > 0) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										7
									
								
								bdk-swift/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								bdk-swift/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,7 +0,0 @@ | ||||
| .DS_Store | ||||
| /.build | ||||
| /Packages | ||||
| /*.xcodeproj | ||||
| xcuserdata/ | ||||
| DerivedData/ | ||||
| .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||||
							
								
								
									
										7
									
								
								bdk-swift/bdk.swift/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								bdk-swift/bdk.swift/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,7 +0,0 @@ | ||||
| .DS_Store | ||||
| /.build | ||||
| /Packages | ||||
| /*.xcodeproj | ||||
| xcuserdata/ | ||||
| DerivedData/ | ||||
| .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||||
| @ -1,16 +0,0 @@ | ||||
| { | ||||
|   "object": { | ||||
|     "pins": [ | ||||
|       { | ||||
|         "package": "Clibbdkffi", | ||||
|         "repositoryURL": "/home/steve/git/notmandatory/Clibbdkffi", | ||||
|         "state": { | ||||
|           "branch": null, | ||||
|           "revision": "9c96e359a3b1e1d5c0db61125147f6ef929bf567", | ||||
|           "version": "0.1.0" | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "version": 1 | ||||
| } | ||||
| @ -1,28 +0,0 @@ | ||||
| // swift-tools-version:5.5 | ||||
| // The swift-tools-version declares the minimum version of Swift required to build this package. | ||||
| 
 | ||||
| import PackageDescription | ||||
| 
 | ||||
| let package = Package( | ||||
|     name: "bdk.swift", | ||||
|     products: [ | ||||
|         // Products define the executables and libraries a package produces, and make them visible to other packages. | ||||
|         .library( | ||||
|             name: "bdk.swift", | ||||
|             targets: ["bdk.swift"]), | ||||
|     ], | ||||
|     dependencies: [ | ||||
|         // Dependencies declare other packages that this package depends on. | ||||
|         .package(url: "../../../Clibbdkffi", from: "0.1.0"), | ||||
|     ], | ||||
|     targets: [ | ||||
|         // Targets are the basic building blocks of a package. A target can define a module or a test suite. | ||||
|         // Targets can depend on other targets in this package, and on products in packages this package depends on.         | ||||
|         .target( | ||||
|             name: "bdk.swift", | ||||
|             dependencies: ["Clibbdkffi"]), | ||||
|         .testTarget( | ||||
|             name: "bdk.swiftTests", | ||||
|             dependencies: ["bdk.swift"]), | ||||
|     ] | ||||
| ) | ||||
| @ -1,11 +0,0 @@ | ||||
| # bdk.swift | ||||
| 
 | ||||
| To build: | ||||
| ``` | ||||
| swift build -Xlinker -L../../target/debug | ||||
| ``` | ||||
| 
 | ||||
| To test: | ||||
| ``` | ||||
| swift test -Xlinker -L../../target/debug | ||||
| ``` | ||||
| @ -1,8 +0,0 @@ | ||||
| import Clibbdkffi | ||||
| 
 | ||||
| public struct bdk_swift { | ||||
|     public private(set) var text = "Hello, World!" | ||||
| 
 | ||||
|     public init() { | ||||
|     } | ||||
| } | ||||
| @ -1,35 +0,0 @@ | ||||
| import XCTest | ||||
| import Clibbdkffi | ||||
| 
 | ||||
| @testable import bdk_swift | ||||
| 
 | ||||
| final class bdk_swiftTests: XCTestCase { | ||||
|     func testExample() throws { | ||||
|         // This is an example of a functional test case. | ||||
|         // Use XCTAssert and related functions to verify your tests produce the correct | ||||
|         // results. | ||||
|         XCTAssertEqual(bdk_swift().text, "Hello, World!") | ||||
|         let desc = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/0/*)"; | ||||
|         let change = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/1/*)"; | ||||
|         let net = "testnet"; | ||||
|         let blocks = "ssl://electrum.blockstream.info:60002"; | ||||
|              | ||||
|         let bc_config = new_electrum_config(blocks, nil, 5, 30, 100) | ||||
|         let db_config = new_memory_config() | ||||
|          | ||||
|         let wallet_result = new_wallet_result(desc,change,net,bc_config,db_config) | ||||
|          | ||||
|         free_blockchain_config(bc_config) | ||||
|         free_database_config(db_config)        | ||||
|          | ||||
|         let wallet = wallet_result.ok | ||||
|         let sync_result = sync_wallet(wallet)    | ||||
|         assert(sync_result.err == FFI_ERROR_NONE)  | ||||
|         free_void_result(sync_result)  | ||||
|          | ||||
|         let address1_result = new_address(wallet).ok | ||||
|         let address1 = String(cString: address1_result!, encoding: .utf8) | ||||
|         //print("address1 = \(address1!)") | ||||
|         assert(address1! == "tb1qh4ajvhz9nd76tqddnl99l89hx4dat33hrjauzw") | ||||
|     } | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| @ -19,7 +19,7 @@ class LibTest { | ||||
|         val address = wallet.getNewAddress() | ||||
|         println("address:" + address) | ||||
|         assertNotNull(address) | ||||
|         // log.debug("address created from kotlin: $address") | ||||
|         // log.debug("address created from bdk-kotlin: $address") | ||||
|         assertEquals(address, "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs") | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								build.sh
									
									
									
									
									
								
							| @ -32,27 +32,27 @@ build_cc() { | ||||
|   cc cc/bdk_ffi_test.c -o cc/bdk_ffi_test -L target/debug -l bdk_ffi -l pthread -l dl -l m | ||||
| } | ||||
| 
 | ||||
| ## copy to bdk-kotlin | ||||
| ## copy to bdk-bdk-kotlin | ||||
| copy_lib_kotlin() { | ||||
|   echo -n "Copy " | ||||
|   case $OS in | ||||
|     "Darwin") | ||||
|       echo -n "darwin " | ||||
|       mkdir -p bdk-kotlin/jvm/src/main/resources/darwin-x86-64 | ||||
|       cp target/debug/libbdk_ffi.dylib bdk-kotlin/jvm/src/main/resources/darwin-x86-64 | ||||
|       mkdir -p bdk-bdk-kotlin/jvm/src/main/resources/darwin-x86-64 | ||||
|       cp target/debug/libbdk_ffi.dylib bdk-bdk-kotlin/jvm/src/main/resources/darwin-x86-64 | ||||
|       ;; | ||||
|     "Linux") | ||||
|       echo -n "linux " | ||||
|       mkdir -p bdk-kotlin/jvm/src/main/resources/linux-x86-64 | ||||
|       cp target/debug/libbdk_ffi.so bdk-kotlin/jvm/src/main/resources/linux-x86-64 | ||||
|       mkdir -p bdk-bdk-kotlin/jvm/src/main/resources/linux-x86-64 | ||||
|       cp target/debug/libbdk_ffi.so bdk-bdk-kotlin/jvm/src/main/resources/linux-x86-64 | ||||
|       ;; | ||||
|   esac | ||||
|   echo "libs to kotlin sub-project" | ||||
| } | ||||
| 
 | ||||
| ## bdk-kotlin jar | ||||
| ## bdk-bdk-kotlin jar | ||||
| build_kotlin() { | ||||
|   (cd bdk-kotlin && ./gradlew :jvm:build && ./gradlew :jvm:publishToMavenLocal) | ||||
|   (cd bdk-bdk-kotlin && ./gradlew :jvm:build && ./gradlew :jvm:publishToMavenLocal) | ||||
| } | ||||
| 
 | ||||
| ## rust android | ||||
| @ -69,27 +69,27 @@ build_android() { | ||||
|   # IMPORTANT: make sure every target is not a substring of a different one. We check for them with grep later on | ||||
|   BUILD_TARGETS="${BUILD_TARGETS:-aarch64,armv7,x86_64,i686}" | ||||
| 
 | ||||
|   mkdir -p bdk-kotlin/android/src/main/jniLibs/ bdk-kotlin/android/src/main/jniLibs/arm64-v8a bdk-kotlin/android/src/main/jniLibs/x86_64 bdk-kotlin/android/src/main/jniLibs/armeabi-v7a bdk-kotlin/android/src/main/jniLibs/x86 | ||||
|   mkdir -p bdk-bdk-kotlin/android/src/main/jniLibs/ bdk-bdk-kotlin/android/src/main/jniLibs/arm64-v8a bdk-bdk-kotlin/android/src/main/jniLibs/x86_64 bdk-bdk-kotlin/android/src/main/jniLibs/armeabi-v7a bdk-bdk-kotlin/android/src/main/jniLibs/x86 | ||||
| 
 | ||||
|   if echo $BUILD_TARGETS | grep "aarch64"; then | ||||
|       CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android21-clang" CC="aarch64-linux-android21-clang" cargo build --target=aarch64-linux-android | ||||
|       cp target/aarch64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/arm64-v8a | ||||
|       cp target/aarch64-linux-android/debug/libbdk_ffi.so bdk-bdk-kotlin/android/src/main/jniLibs/arm64-v8a | ||||
|   fi | ||||
|   if echo $BUILD_TARGETS | grep "x86_64"; then | ||||
|       CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="x86_64-linux-android21-clang" CC="x86_64-linux-android21-clang" cargo build --target=x86_64-linux-android | ||||
|       cp target/x86_64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86_64 | ||||
|       cp target/x86_64-linux-android/debug/libbdk_ffi.so bdk-bdk-kotlin/android/src/main/jniLibs/x86_64 | ||||
|   fi | ||||
|   if echo $BUILD_TARGETS | grep "armv7"; then | ||||
|       CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="armv7a-linux-androideabi21-clang" CC="armv7a-linux-androideabi21-clang" cargo build --target=armv7-linux-androideabi | ||||
|       cp target/armv7-linux-androideabi/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/armeabi-v7a | ||||
|       cp target/armv7-linux-androideabi/debug/libbdk_ffi.so bdk-bdk-kotlin/android/src/main/jniLibs/armeabi-v7a | ||||
|   fi | ||||
|   if echo $BUILD_TARGETS | grep "i686"; then | ||||
|       CARGO_TARGET_I686_LINUX_ANDROID_LINKER="i686-linux-android21-clang" CC="i686-linux-android21-clang" cargo build --target=i686-linux-android | ||||
|       cp target/i686-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86 | ||||
|       cp target/i686-linux-android/debug/libbdk_ffi.so bdk-bdk-kotlin/android/src/main/jniLibs/x86 | ||||
|   fi | ||||
| 
 | ||||
|   # bdk-kotlin aar | ||||
|   (cd bdk-kotlin && ./gradlew :android:build && ./gradlew :android:publishToMavenLocal) | ||||
|   # bdk-bdk-kotlin aar | ||||
|   (cd bdk-bdk-kotlin && ./gradlew :android:build && ./gradlew :android:publishToMavenLocal) | ||||
| } | ||||
| 
 | ||||
| OS=$(uname) | ||||
|  | ||||
							
								
								
									
										315
									
								
								cc/bdk_ffi.h
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								cc/bdk_ffi.h
									
									
									
									
									
								
							| @ -1,315 +0,0 @@ | ||||
| /*! \file */ | ||||
| /*******************************************
 | ||||
|  *                                         * | ||||
|  *  File auto-generated by `::safer_ffi`.  * | ||||
|  *                                         * | ||||
|  *  Do not manually edit this file.        * | ||||
|  *                                         * | ||||
|  *******************************************/ | ||||
| 
 | ||||
| #ifndef __RUST_BDK_FFI__ | ||||
| #define __RUST_BDK_FFI__ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| typedef struct DatabaseConfig DatabaseConfig_t; | ||||
| 
 | ||||
| DatabaseConfig_t * new_memory_config (void); | ||||
| 
 | ||||
| DatabaseConfig_t * new_sled_config ( | ||||
|     char const * path, | ||||
|     char const * tree_name); | ||||
| 
 | ||||
| void free_database_config ( | ||||
|     DatabaseConfig_t * database_config); | ||||
| 
 | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /** \remark Has the same ABI as `uint16_t` **/ | ||||
| #ifdef DOXYGEN | ||||
| typedef enum FfiError | ||||
| #else | ||||
| typedef uint16_t FfiError_t; enum | ||||
| #endif | ||||
| { | ||||
|     /** . */ | ||||
|     FFI_ERROR_NONE, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INVALID_U32_BYTES, | ||||
|     /** . */ | ||||
|     FFI_ERROR_GENERIC, | ||||
|     /** . */ | ||||
|     FFI_ERROR_SCRIPT_DOESNT_HAVE_ADDRESS_FORM, | ||||
|     /** . */ | ||||
|     FFI_ERROR_NO_RECIPIENTS, | ||||
|     /** . */ | ||||
|     FFI_ERROR_NO_UTXOS_SELECTED, | ||||
|     /** . */ | ||||
|     FFI_ERROR_OUTPUT_BELOW_DUST_LIMIT, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INSUFFICIENT_FUNDS, | ||||
|     /** . */ | ||||
|     FFI_ERROR_BN_B_TOTAL_TRIES_EXCEEDED, | ||||
|     /** . */ | ||||
|     FFI_ERROR_BN_B_NO_EXACT_MATCH, | ||||
|     /** . */ | ||||
|     FFI_ERROR_UNKNOWN_UTXO, | ||||
|     /** . */ | ||||
|     FFI_ERROR_TRANSACTION_NOT_FOUND, | ||||
|     /** . */ | ||||
|     FFI_ERROR_TRANSACTION_CONFIRMED, | ||||
|     /** . */ | ||||
|     FFI_ERROR_IRREPLACEABLE_TRANSACTION, | ||||
|     /** . */ | ||||
|     FFI_ERROR_FEE_RATE_TOO_LOW, | ||||
|     /** . */ | ||||
|     FFI_ERROR_FEE_TOO_LOW, | ||||
|     /** . */ | ||||
|     FFI_ERROR_FEE_RATE_UNAVAILABLE, | ||||
|     /** . */ | ||||
|     FFI_ERROR_MISSING_KEY_ORIGIN, | ||||
|     /** . */ | ||||
|     FFI_ERROR_KEY, | ||||
|     /** . */ | ||||
|     FFI_ERROR_CHECKSUM_MISMATCH, | ||||
|     /** . */ | ||||
|     FFI_ERROR_SPENDING_POLICY_REQUIRED, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INVALID_POLICY_PATH_ERROR, | ||||
|     /** . */ | ||||
|     FFI_ERROR_SIGNER, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INVALID_NETWORK, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INVALID_PROGRESS_VALUE, | ||||
|     /** . */ | ||||
|     FFI_ERROR_PROGRESS_UPDATE_ERROR, | ||||
|     /** . */ | ||||
|     FFI_ERROR_INVALID_OUTPOINT, | ||||
|     /** . */ | ||||
|     FFI_ERROR_DESCRIPTOR, | ||||
|     /** . */ | ||||
|     FFI_ERROR_ADDRESS_VALIDATOR, | ||||
|     /** . */ | ||||
|     FFI_ERROR_ENCODE, | ||||
|     /** . */ | ||||
|     FFI_ERROR_MINISCRIPT, | ||||
|     /** . */ | ||||
|     FFI_ERROR_BIP32, | ||||
|     /** . */ | ||||
|     FFI_ERROR_SECP256K1, | ||||
|     /** . */ | ||||
|     FFI_ERROR_JSON, | ||||
|     /** . */ | ||||
|     FFI_ERROR_HEX, | ||||
|     /** . */ | ||||
|     FFI_ERROR_PSBT, | ||||
|     /** . */ | ||||
|     FFI_ERROR_PSBT_PARSE, | ||||
|     /** . */ | ||||
|     FFI_ERROR_ELECTRUM, | ||||
|     /** . */ | ||||
|     FFI_ERROR_SLED, | ||||
| } | ||||
| #ifdef DOXYGEN | ||||
| 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); | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     uint64_t ok; | ||||
| 
 | ||||
|     FfiError_t err; | ||||
| 
 | ||||
| } FfiResult_uint64_t; | ||||
| 
 | ||||
| void free_uint64_result ( | ||||
|     FfiResult_uint64_t void_result); | ||||
| 
 | ||||
| /** \brief
 | ||||
|  *  Free a Rust-allocated string | ||||
|  */ | ||||
| void free_string ( | ||||
|     char * string); | ||||
| 
 | ||||
| typedef struct BlockchainConfig BlockchainConfig_t; | ||||
| 
 | ||||
| typedef struct OpaqueWallet OpaqueWallet_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     OpaqueWallet_t * ok; | ||||
| 
 | ||||
|     FfiError_t err; | ||||
| 
 | ||||
| } FfiResult_OpaqueWallet_ptr_t; | ||||
| 
 | ||||
| FfiResult_OpaqueWallet_ptr_t new_wallet_result ( | ||||
|     char const * descriptor, | ||||
|     char const * change_descriptor, | ||||
|     char const * network, | ||||
|     BlockchainConfig_t const * blockchain_config, | ||||
|     DatabaseConfig_t const * database_config); | ||||
| 
 | ||||
| void free_wallet_result ( | ||||
|     FfiResult_OpaqueWallet_ptr_t wallet_result); | ||||
| 
 | ||||
| FfiResultVoid_t sync_wallet ( | ||||
|     OpaqueWallet_t const * opaque_wallet); | ||||
| 
 | ||||
| FfiResult_char_ptr_t new_address ( | ||||
|     OpaqueWallet_t const * opaque_wallet); | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     char * txid; | ||||
| 
 | ||||
|     uint32_t vout; | ||||
| 
 | ||||
| } OutPoint_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     uint64_t value; | ||||
| 
 | ||||
|     char * script_pubkey; | ||||
| 
 | ||||
| } TxOut_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     OutPoint_t outpoint; | ||||
| 
 | ||||
|     TxOut_t txout; | ||||
| 
 | ||||
|     uint16_t keychain; | ||||
| 
 | ||||
| } LocalUtxo_t; | ||||
| 
 | ||||
| /** \brief
 | ||||
|  *  Same as [`Vec<T>`][`rust::Vec`], but with guaranteed `#[repr(C)]` layout | ||||
|  */ | ||||
| typedef struct { | ||||
| 
 | ||||
|     LocalUtxo_t * ptr; | ||||
| 
 | ||||
|     size_t len; | ||||
| 
 | ||||
|     size_t cap; | ||||
| 
 | ||||
| } Vec_LocalUtxo_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     Vec_LocalUtxo_t ok; | ||||
| 
 | ||||
|     FfiError_t err; | ||||
| 
 | ||||
| } FfiResult_Vec_LocalUtxo_t; | ||||
| 
 | ||||
| FfiResult_Vec_LocalUtxo_t list_unspent ( | ||||
|     OpaqueWallet_t const * opaque_wallet); | ||||
| 
 | ||||
| void free_veclocalutxo_result ( | ||||
|     FfiResult_Vec_LocalUtxo_t unspent_result); | ||||
| 
 | ||||
| FfiResult_uint64_t balance ( | ||||
|     OpaqueWallet_t const * opaque_wallet); | ||||
| 
 | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     uint32_t height; | ||||
| 
 | ||||
|     uint64_t timestamp; | ||||
| 
 | ||||
| } ConfirmationTime_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     char * txid; | ||||
| 
 | ||||
|     uint64_t received; | ||||
| 
 | ||||
|     uint64_t sent; | ||||
| 
 | ||||
|     int64_t fee; | ||||
| 
 | ||||
|     bool is_confirmed; | ||||
| 
 | ||||
|     ConfirmationTime_t confirmation_time; | ||||
| 
 | ||||
|     bool verified; | ||||
| 
 | ||||
| } TransactionDetails_t; | ||||
| 
 | ||||
| /** \brief
 | ||||
|  *  Same as [`Vec<T>`][`rust::Vec`], but with guaranteed `#[repr(C)]` layout | ||||
|  */ | ||||
| typedef struct { | ||||
| 
 | ||||
|     TransactionDetails_t * ptr; | ||||
| 
 | ||||
|     size_t len; | ||||
| 
 | ||||
|     size_t cap; | ||||
| 
 | ||||
| } Vec_TransactionDetails_t; | ||||
| 
 | ||||
| typedef struct { | ||||
| 
 | ||||
|     Vec_TransactionDetails_t ok; | ||||
| 
 | ||||
|     FfiError_t err; | ||||
| 
 | ||||
| } FfiResult_Vec_TransactionDetails_t; | ||||
| 
 | ||||
| FfiResult_Vec_TransactionDetails_t list_transactions ( | ||||
|     OpaqueWallet_t const * opaque_wallet); | ||||
| 
 | ||||
| void free_vectxdetails_result ( | ||||
|     FfiResult_Vec_TransactionDetails_t txdetails_result); | ||||
| 
 | ||||
| BlockchainConfig_t * new_electrum_config ( | ||||
|     char const * url, | ||||
|     char const * socks5, | ||||
|     int16_t retry, | ||||
|     int16_t timeout, | ||||
|     size_t stop_gap); | ||||
| 
 | ||||
| void free_blockchain_config ( | ||||
|     BlockchainConfig_t * blockchain_config); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
| 
 | ||||
| #endif /* __RUST_BDK_FFI__ */ | ||||
| @ -1,199 +0,0 @@ | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include "bdk_ffi.h" | ||||
| 
 | ||||
| int main (int argc, char const * const argv[]) | ||||
| {    | ||||
| 
 | ||||
|     // shared consts
 | ||||
|     char const *desc = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/0/*)"; | ||||
|     char const *change = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/1/*)"; | ||||
|     char const *net = "testnet"; | ||||
|     char const *blocks = "ssl://electrum.blockstream.info:60002"; | ||||
|          | ||||
|     // test new wallet error
 | ||||
|     {  | ||||
|         BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); | ||||
|         //DatabaseConfig_t *db_config = new_sled_config("/home/steve/.bdk", "test_wallet");
 | ||||
|         DatabaseConfig_t *db_config = new_memory_config(); | ||||
|          | ||||
|         // new wallet with bad descriptor 
 | ||||
|         FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result("bad","bad",net,bc_config,db_config);   | ||||
|         assert(wallet_result.err == FFI_ERROR_DESCRIPTOR); | ||||
|         assert(wallet_result.ok == NULL); | ||||
|          | ||||
|         free_blockchain_config(bc_config); | ||||
|         free_database_config(db_config);   | ||||
|          | ||||
|         free_wallet_result(wallet_result); | ||||
|     } | ||||
|      | ||||
|     // test new wallet
 | ||||
|     { | ||||
|         BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); | ||||
|         DatabaseConfig_t *db_config = new_memory_config(); | ||||
|      | ||||
|         // new wallet
 | ||||
|         FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); | ||||
|         // printf("wallet_result.err = %d\n", wallet_result.err));
 | ||||
|         assert(wallet_result.err == FFI_ERROR_NONE);     | ||||
|         assert(wallet_result.ok != NULL); | ||||
|          | ||||
|         free_blockchain_config(bc_config); | ||||
|         free_database_config(db_config); | ||||
|          | ||||
|         OpaqueWallet_t *wallet = wallet_result.ok; | ||||
|          | ||||
|         // sync wallet
 | ||||
|         FfiResultVoid_t sync_result = sync_wallet(wallet);    | ||||
|         assert(sync_result.err == FFI_ERROR_NONE);     | ||||
|         free_void_result(sync_result); | ||||
|          | ||||
|         // new address
 | ||||
|         FfiResult_char_ptr_t address1_result = new_address(wallet); | ||||
|         assert(address1_result.ok != NULL); | ||||
|         assert(address1_result.err == FFI_ERROR_NONE); | ||||
|         //printf("address1 = %s\n", address1_result.ok);
 | ||||
|         assert( 0 == strcmp(address1_result.ok,"tb1qh4ajvhz9nd76tqddnl99l89hx4dat33hrjauzw")); | ||||
|         free_string_result(address1_result); | ||||
|          | ||||
|         FfiResult_char_ptr_t address2_result = new_address(wallet); | ||||
|         assert(address2_result.ok != NULL); | ||||
|         assert(address2_result.err == FFI_ERROR_NONE); | ||||
|         //printf("address2 = %s\n", address2_result.ok);
 | ||||
|         assert( 0 == strcmp(address2_result.ok,"tb1qr7pu0pech43hcjrc4pzxcen0qkslj7xk7s5w3m")); | ||||
|         free_string_result(address2_result); | ||||
|          | ||||
|         // free_wallet
 | ||||
|         free_wallet_result(wallet_result); | ||||
|          | ||||
|         // verify free_wallet after free_wallet fails (core dumped)
 | ||||
|         //// free_wallet_result(wallet_result);
 | ||||
|          | ||||
|         // verify sync_wallet after free_wallet fails (core dumped)
 | ||||
|         //// FfiResultVoid_t sync_result2 = sync_wallet(wallet);    
 | ||||
|     } | ||||
|      | ||||
|     // test get unspent utxos
 | ||||
|     {     | ||||
|         BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); | ||||
|         DatabaseConfig_t *db_config = new_memory_config(); | ||||
|      | ||||
|         // new wallet
 | ||||
|         FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); | ||||
|         assert(wallet_result.err == FFI_ERROR_NONE);     | ||||
|         assert(wallet_result.ok != NULL); | ||||
|          | ||||
|         free_blockchain_config(bc_config); | ||||
|         free_database_config(db_config); | ||||
|          | ||||
|         OpaqueWallet_t *wallet = wallet_result.ok; | ||||
|          | ||||
|         // sync wallet
 | ||||
|         FfiResultVoid_t sync_result = sync_wallet(wallet);    | ||||
|         assert(sync_result.err == FFI_ERROR_NONE);     | ||||
|         free_void_result(sync_result); | ||||
|          | ||||
|         // list unspent
 | ||||
|         FfiResult_Vec_LocalUtxo_t unspent_result = list_unspent(wallet); | ||||
|         assert(unspent_result.ok.len == 1); | ||||
|         assert(unspent_result.err == FFI_ERROR_NONE); | ||||
|          | ||||
|         LocalUtxo_t * unspent_ptr = unspent_result.ok.ptr;                        | ||||
|         for (int i = 0; i < unspent_result.ok.len; i++) {             | ||||
|             // printf("%d: outpoint.txid: %s\n", i, unspent_ptr[i].outpoint.txid);
 | ||||
|             assert(unspent_ptr[i].outpoint.txid != NULL); | ||||
|             // printf("%d: outpoint.vout: %d\n", i, unspent_ptr[i].outpoint.vout);
 | ||||
|             assert(unspent_ptr[i].outpoint.vout >= 0); | ||||
|             // printf("%d: txout.value: %ld\n", i, unspent_ptr[i].txout.value);
 | ||||
|             assert(unspent_ptr[i].txout.value > 0); | ||||
|             // printf("%d: txout.script_pubkey: %s\n", i, unspent_ptr[i].txout.script_pubkey);
 | ||||
|             assert(unspent_ptr[i].txout.script_pubkey != NULL); | ||||
|             // printf("%d: keychain: %d\n", i, unspent_ptr[i].keychain);
 | ||||
|             assert(unspent_ptr[i].keychain >= 0); | ||||
|         } | ||||
|          | ||||
|         free_veclocalutxo_result(unspent_result);           | ||||
|         free_wallet_result(wallet_result); | ||||
|     } | ||||
|      | ||||
|     // test balance 
 | ||||
|     {         | ||||
|         BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); | ||||
|         DatabaseConfig_t *db_config = new_memory_config(); | ||||
|      | ||||
|         // new wallet
 | ||||
|         FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); | ||||
|         assert(wallet_result.err == FFI_ERROR_NONE);     | ||||
|         assert(wallet_result.ok != NULL); | ||||
|          | ||||
|         free_blockchain_config(bc_config); | ||||
|         free_database_config(db_config); | ||||
|          | ||||
|         OpaqueWallet_t *wallet = wallet_result.ok; | ||||
|          | ||||
|         // sync wallet
 | ||||
|         FfiResultVoid_t sync_result = sync_wallet(wallet);    | ||||
|         assert(sync_result.err == FFI_ERROR_NONE);     | ||||
|         free_void_result(sync_result); | ||||
|          | ||||
|         // get balance
 | ||||
|         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_uint64_result(balance_result); | ||||
|         free_wallet_result(wallet_result); | ||||
|     } | ||||
|      | ||||
|     // test get transaction details
 | ||||
|     { | ||||
|         BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); | ||||
|         DatabaseConfig_t *db_config = new_memory_config(); | ||||
|      | ||||
|         // new wallet
 | ||||
|         FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); | ||||
|         assert(wallet_result.err == FFI_ERROR_NONE);     | ||||
|         assert(wallet_result.ok != NULL); | ||||
|          | ||||
|         free_blockchain_config(bc_config); | ||||
|         free_database_config(db_config); | ||||
|          | ||||
|         OpaqueWallet_t *wallet = wallet_result.ok; | ||||
|          | ||||
|         // sync wallet
 | ||||
|         FfiResultVoid_t sync_result = sync_wallet(wallet);    | ||||
|         assert(sync_result.err == FFI_ERROR_NONE);     | ||||
|         free_void_result(sync_result); | ||||
|          | ||||
|         // list transactions
 | ||||
|         FfiResult_Vec_TransactionDetails_t txdetails_result = list_transactions(wallet); | ||||
|         assert(txdetails_result.ok.len > 0); | ||||
|         assert(txdetails_result.err == FFI_ERROR_NONE); | ||||
|          | ||||
|         TransactionDetails_t * txdetails_ptr = txdetails_result.ok.ptr;                        | ||||
|         for (int i = 0; i < txdetails_result.ok.len; i++) {             | ||||
|             //printf("%d: txid: %s\n", i, txdetails_ptr[i].txid);
 | ||||
|             assert(txdetails_ptr[i].txid != NULL); | ||||
|             //printf("%d: timestamp: %ld\n", i, txdetails_ptr[i].timestamp);
 | ||||
|             assert(txdetails_ptr[i].is_confirmed); | ||||
|             //printf("%d: received: %ld\n", i, txdetails_ptr[i].received);
 | ||||
|             //printf("%d: sent: %ld\n", i, txdetails_ptr[i].sent);
 | ||||
|             assert(txdetails_ptr[i].received > 0 || txdetails_ptr[i].sent > 0); | ||||
|             //printf("%d: fees: %ld\n", i, txdetails_ptr[i].fees);
 | ||||
|             assert(txdetails_ptr[i].fee > 0); | ||||
|             //printf("%d: height: %d\n", i, txdetails_ptr[i].height);
 | ||||
|             assert(txdetails_ptr[i].confirmation_time.height > 0); | ||||
|         } | ||||
|          | ||||
|         free_vectxdetails_result(txdetails_result);           | ||||
|         free_wallet_result(wallet_result); | ||||
|     } | ||||
|          | ||||
|     return EXIT_SUCCESS; | ||||
| } | ||||
							
								
								
									
										134
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/error.rs
									
									
									
									
									
								
							| @ -1,134 +0,0 @@ | ||||
| use bdk::Error; | ||||
| use thiserror::Error; | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
| pub enum FfiError { | ||||
|     #[error("data store disconnected")] | ||||
|     None, | ||||
|     #[error("data store disconnected")] | ||||
|     InvalidU32Bytes, | ||||
|     #[error("data store disconnected")] | ||||
|     Generic, | ||||
|     #[error("data store disconnected")] | ||||
|     ScriptDoesntHaveAddressForm, | ||||
|     #[error("data store disconnected")] | ||||
|     NoRecipients, | ||||
|     #[error("data store disconnected")] | ||||
|     NoUtxosSelected, | ||||
|     #[error("data store disconnected")] | ||||
|     OutputBelowDustLimit, | ||||
|     #[error("data store disconnected")] | ||||
|     InsufficientFunds, | ||||
|     #[error("data store disconnected")] | ||||
|     BnBTotalTriesExceeded, | ||||
|     #[error("data store disconnected")] | ||||
|     BnBNoExactMatch, | ||||
|     #[error("data store disconnected")] | ||||
|     UnknownUtxo, | ||||
|     #[error("data store disconnected")] | ||||
|     TransactionNotFound, | ||||
|     #[error("data store disconnected")] | ||||
|     TransactionConfirmed, | ||||
|     #[error("data store disconnected")] | ||||
|     IrreplaceableTransaction, | ||||
|     #[error("data store disconnected")] | ||||
|     FeeRateTooLow, | ||||
|     #[error("data store disconnected")] | ||||
|     FeeTooLow, | ||||
|     #[error("data store disconnected")] | ||||
|     FeeRateUnavailable, | ||||
|     #[error("data store disconnected")] | ||||
|     MissingKeyOrigin, | ||||
|     #[error("data store disconnected")] | ||||
|     Key, | ||||
|     #[error("data store disconnected")] | ||||
|     ChecksumMismatch, | ||||
|     #[error("data store disconnected")] | ||||
|     SpendingPolicyRequired, | ||||
|     #[error("data store disconnected")] | ||||
|     InvalidPolicyPathError, | ||||
|     #[error("data store disconnected")] | ||||
|     Signer, | ||||
|     #[error("data store disconnected")] | ||||
|     InvalidNetwork, | ||||
|     #[error("data store disconnected")] | ||||
|     InvalidProgressValue, | ||||
|     #[error("data store disconnected")] | ||||
|     ProgressUpdateError, | ||||
|     #[error("data store disconnected")] | ||||
|     InvalidOutpoint, | ||||
|     #[error("data store disconnected")] | ||||
|     Descriptor, | ||||
|     #[error("data store disconnected")] | ||||
|     AddressValidator, | ||||
|     #[error("data store disconnected")] | ||||
|     Encode, | ||||
|     #[error("data store disconnected")] | ||||
|     Miniscript, | ||||
|     #[error("data store disconnected")] | ||||
|     Bip32, | ||||
|     #[error("data store disconnected")] | ||||
|     Secp256k1, | ||||
|     #[error("data store disconnected")] | ||||
|     Json, | ||||
|     #[error("data store disconnected")] | ||||
|     Hex, | ||||
|     #[error("data store disconnected")] | ||||
|     Psbt, | ||||
|     #[error("data store disconnected")] | ||||
|     PsbtParse, | ||||
|     #[error("data store disconnected")] | ||||
|     Electrum, | ||||
|     // Esplora,
 | ||||
|     // CompactFilters,
 | ||||
|     #[error("data store disconnected")] | ||||
|     Sled, | ||||
| } | ||||
| 
 | ||||
| impl From<bdk::Error> for FfiError { | ||||
|     fn from(error: bdk::Error) -> Self { | ||||
|         match error { | ||||
|             Error::InvalidU32Bytes(_) => FfiError::InvalidU32Bytes, | ||||
|             Error::Generic(_) => FfiError::Generic, | ||||
|             Error::ScriptDoesntHaveAddressForm => FfiError::ScriptDoesntHaveAddressForm, | ||||
|             Error::NoRecipients => FfiError::NoRecipients, | ||||
|             Error::NoUtxosSelected => FfiError::NoUtxosSelected, | ||||
|             Error::OutputBelowDustLimit(_) => FfiError::OutputBelowDustLimit, | ||||
|             Error::InsufficientFunds { .. } => FfiError::InsufficientFunds, | ||||
|             Error::BnBTotalTriesExceeded => FfiError::BnBTotalTriesExceeded, | ||||
|             Error::BnBNoExactMatch => FfiError::BnBNoExactMatch, | ||||
|             Error::UnknownUtxo => FfiError::UnknownUtxo, | ||||
|             Error::TransactionNotFound => FfiError::TransactionNotFound, | ||||
|             Error::TransactionConfirmed => FfiError::TransactionConfirmed, | ||||
|             Error::IrreplaceableTransaction => FfiError::IrreplaceableTransaction, | ||||
|             Error::FeeRateTooLow { .. } => FfiError::FeeRateTooLow, | ||||
|             Error::FeeTooLow { .. } => FfiError::FeeTooLow, | ||||
|             Error::FeeRateUnavailable => FfiError::FeeRateUnavailable, | ||||
|             Error::MissingKeyOrigin(_) => FfiError::MissingKeyOrigin, | ||||
|             Error::Key(_) => FfiError::Key, | ||||
|             Error::ChecksumMismatch => FfiError::ChecksumMismatch, | ||||
|             Error::SpendingPolicyRequired(_) => FfiError::SpendingPolicyRequired, | ||||
|             Error::InvalidPolicyPathError(_) => FfiError::InvalidPolicyPathError, | ||||
|             Error::Signer(_) => FfiError::Signer, | ||||
|             Error::InvalidNetwork { .. } => FfiError::InvalidNetwork, | ||||
|             Error::InvalidProgressValue(_) => FfiError::InvalidProgressValue, | ||||
|             Error::ProgressUpdateError => FfiError::ProgressUpdateError, | ||||
|             Error::InvalidOutpoint(_) => FfiError::InvalidOutpoint, | ||||
|             Error::Descriptor(_) => FfiError::Descriptor, | ||||
|             Error::AddressValidator(_) => FfiError::AddressValidator, | ||||
|             Error::Encode(_) => FfiError::Encode, | ||||
|             Error::Miniscript(_) => FfiError::Miniscript, | ||||
|             Error::Bip32(_) => FfiError::Bip32, | ||||
|             Error::Secp256k1(_) => FfiError::Secp256k1, | ||||
|             Error::Json(_) => FfiError::Json, | ||||
|             Error::Hex(_) => FfiError::Hex, | ||||
|             Error::Psbt(_) => FfiError::Psbt, | ||||
|             Error::PsbtParse(_) => FfiError::PsbtParse, | ||||
|             Error::Electrum(_) => FfiError::Electrum, | ||||
|             //        Error::Esplora(_) => JniError::Esplora,
 | ||||
|             //        Error::CompactFilters(_) => JniError::CompactFilters,
 | ||||
|             //        Error::Rpc(_) => JniError::Rpc,
 | ||||
|             Error::Sled(_) => FfiError::Sled, | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/types.rs
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								src/types.rs
									
									
									
									
									
								
							| @ -1,40 +0,0 @@ | ||||
| use crate::error::FfiError; | ||||
| use ::safer_ffi::prelude::*; | ||||
| use safer_ffi::char_p::char_p_boxed; | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug)] | ||||
| pub struct FfiResult<T> { | ||||
|     pub ok: T, | ||||
|     pub err: FfiError, | ||||
| } | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug)] | ||||
| pub struct FfiResultVoid { | ||||
|     pub err: FfiError, | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_string_result(string_result: FfiResult<char_p_boxed>) { | ||||
|     drop(string_result) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_void_result(void_result: FfiResultVoid) { | ||||
|     drop(void_result) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_uint64_result(void_result: FfiResult<u64>) { | ||||
|     drop(void_result) | ||||
| } | ||||
| 
 | ||||
| // TODO do we need this? remove?
 | ||||
| /// Free a Rust-allocated string
 | ||||
| #[ffi_export] | ||||
| fn free_string(string: Option<char_p_boxed>) { | ||||
|     drop(string) | ||||
| } | ||||
| @ -1,602 +0,0 @@ | ||||
| // This file was autogenerated by some hot garbage in the `uniffi` crate. | ||||
| // Trust me, you don't want to mess with it! | ||||
| 
 | ||||
| @file:Suppress("NAME_SHADOWING") | ||||
| 
 | ||||
| package uniffi.bdk; | ||||
| 
 | ||||
| // Common helper code. | ||||
| // | ||||
| // Ideally this would live in a separate .kt file where it can be unittested etc | ||||
| // in isolation, and perhaps even published as a re-useable package. | ||||
| // | ||||
| // However, it's important that the detils of how this helper code works (e.g. the | ||||
| // way that different builtin types are passed across the FFI) exactly match what's | ||||
| // expected by the Rust code on the other side of the interface. In practice right | ||||
| // now that means coming from the exact some version of `uniffi` that was used to | ||||
| // compile the Rust component. The easiest way to ensure this is to bundle the Kotlin | ||||
| // helpers directly inline like we're doing here. | ||||
| 
 | ||||
| import com.sun.jna.Library | ||||
| import com.sun.jna.Native | ||||
| import com.sun.jna.Pointer | ||||
| import com.sun.jna.Structure | ||||
| import java.nio.ByteBuffer | ||||
| import java.nio.ByteOrder | ||||
| import java.util.concurrent.atomic.AtomicLong | ||||
| import java.util.concurrent.atomic.AtomicBoolean | ||||
| import java.util.concurrent.atomic.AtomicReference | ||||
| import java.util.concurrent.locks.ReentrantLock | ||||
| import kotlin.concurrent.withLock | ||||
| 
 | ||||
| // This is a helper for safely working with byte buffers returned from the Rust code. | ||||
| // A rust-owned buffer is represented by its capacity, its current length, and a | ||||
| // pointer to the underlying data. | ||||
| 
 | ||||
| @Structure.FieldOrder("capacity", "len", "data") | ||||
| open class RustBuffer : Structure() { | ||||
|     @JvmField var capacity: Int = 0 | ||||
|     @JvmField var len: Int = 0 | ||||
|     @JvmField var data: Pointer? = null | ||||
| 
 | ||||
|     class ByValue : RustBuffer(), Structure.ByValue | ||||
|     class ByReference : RustBuffer(), Structure.ByReference | ||||
| 
 | ||||
|     companion object { | ||||
|         internal fun alloc(size: Int = 0) = rustCall() { status -> | ||||
|             _UniFFILib.INSTANCE.ffi_bdk_ed55_rustbuffer_alloc(size, status) | ||||
|         } | ||||
| 
 | ||||
|         internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> | ||||
|             _UniFFILib.INSTANCE.ffi_bdk_ed55_rustbuffer_free(buf, status) | ||||
|         } | ||||
| 
 | ||||
|         internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status -> | ||||
|             _UniFFILib.INSTANCE.ffi_bdk_ed55_rustbuffer_reserve(buf, additional, status) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Suppress("TooGenericExceptionThrown") | ||||
|     fun asByteBuffer() = | ||||
|         this.data?.getByteBuffer(0, this.len.toLong())?.also { | ||||
|             it.order(ByteOrder.BIG_ENDIAN) | ||||
|         } | ||||
| } | ||||
| 
 | ||||
| // This is a helper for safely passing byte references into the rust code. | ||||
| // It's not actually used at the moment, because there aren't many things that you | ||||
| // can take a direct pointer to in the JVM, and if we're going to copy something | ||||
| // then we might as well copy it into a `RustBuffer`. But it's here for API | ||||
| // completeness. | ||||
| 
 | ||||
| @Structure.FieldOrder("len", "data") | ||||
| open class ForeignBytes : Structure() { | ||||
|     @JvmField var len: Int = 0 | ||||
|     @JvmField var data: Pointer? = null | ||||
| 
 | ||||
|     class ByValue : ForeignBytes(), Structure.ByValue | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // A helper for structured writing of data into a `RustBuffer`. | ||||
| // This is very similar to `java.nio.ByteBuffer` but it knows how to grow | ||||
| // the underlying `RustBuffer` on demand. | ||||
| // | ||||
| // TODO: we should benchmark writing things into a `RustBuffer` versus building | ||||
| // up a bytearray and then copying it across. | ||||
| 
 | ||||
| class RustBufferBuilder() { | ||||
|     var rbuf = RustBuffer.ByValue() | ||||
|     var bbuf: ByteBuffer? = null | ||||
| 
 | ||||
|     init { | ||||
|         val rbuf = RustBuffer.alloc(16) // Totally arbitrary initial size | ||||
|         rbuf.writeField("len", 0) | ||||
|         this.setRustBuffer(rbuf) | ||||
|     } | ||||
| 
 | ||||
|     internal fun setRustBuffer(rbuf: RustBuffer.ByValue) { | ||||
|         this.rbuf = rbuf | ||||
|         this.bbuf = this.rbuf.data?.getByteBuffer(0, this.rbuf.capacity.toLong())?.also { | ||||
|             it.order(ByteOrder.BIG_ENDIAN) | ||||
|             it.position(rbuf.len) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun finalize() : RustBuffer.ByValue { | ||||
|         val rbuf = this.rbuf | ||||
|         // Ensure that the JVM-level field is written through to native memory | ||||
|         // before turning the buffer, in case its recipient uses it in a context | ||||
|         // JNA doesn't apply its automatic synchronization logic. | ||||
|         rbuf.writeField("len", this.bbuf!!.position()) | ||||
|         this.setRustBuffer(RustBuffer.ByValue()) | ||||
|         return rbuf | ||||
|     } | ||||
| 
 | ||||
|     fun discard() { | ||||
|         val rbuf = this.finalize() | ||||
|         RustBuffer.free(rbuf) | ||||
|     } | ||||
| 
 | ||||
|     internal fun reserve(size: Int, write: (ByteBuffer) -> Unit) { | ||||
|         // TODO: this will perform two checks to ensure we're not overflowing the buffer: | ||||
|         // one here where we check if it needs to grow, and another when we call a write | ||||
|         // method on the ByteBuffer. It might be cheaper to use exception-driven control-flow | ||||
|         // here, trying the write and growing if it throws a `BufferOverflowException`. | ||||
|         // Benchmarking needed. | ||||
|         if (this.bbuf!!.position() + size > this.rbuf.capacity) { | ||||
|             rbuf.writeField("len", this.bbuf!!.position()) | ||||
|             this.setRustBuffer(RustBuffer.reserve(this.rbuf, size)) | ||||
|         } | ||||
|         write(this.bbuf!!) | ||||
|     } | ||||
| 
 | ||||
|     fun putByte(v: Byte) { | ||||
|         this.reserve(1) { bbuf -> | ||||
|             bbuf.put(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun putShort(v: Short) { | ||||
|         this.reserve(2) { bbuf -> | ||||
|             bbuf.putShort(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun putInt(v: Int) { | ||||
|         this.reserve(4) { bbuf -> | ||||
|             bbuf.putInt(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun putLong(v: Long) { | ||||
|         this.reserve(8) { bbuf -> | ||||
|             bbuf.putLong(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun putFloat(v: Float) { | ||||
|         this.reserve(4) { bbuf -> | ||||
|             bbuf.putFloat(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun putDouble(v: Double) { | ||||
|         this.reserve(8) { bbuf -> | ||||
|             bbuf.putDouble(v) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun put(v: ByteArray) { | ||||
|         this.reserve(v.size) { bbuf -> | ||||
|             bbuf.put(v) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Helpers for reading primitive data types from a bytebuffer. | ||||
| 
 | ||||
| internal fun<T> liftFromRustBuffer(rbuf: RustBuffer.ByValue, readItem: (ByteBuffer) -> T): T { | ||||
|     val buf = rbuf.asByteBuffer()!! | ||||
|     try { | ||||
|        val item = readItem(buf) | ||||
|        if (buf.hasRemaining()) { | ||||
|            throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") | ||||
|        } | ||||
|        return item | ||||
|     } finally { | ||||
|         RustBuffer.free(rbuf) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal fun<T> lowerIntoRustBuffer(v: T, writeItem: (T, RustBufferBuilder) -> Unit): RustBuffer.ByValue { | ||||
|     // TODO: maybe we can calculate some sort of initial size hint? | ||||
|     val buf = RustBufferBuilder() | ||||
|     try { | ||||
|         writeItem(v, buf) | ||||
|         return buf.finalize() | ||||
|     } catch (e: Throwable) { | ||||
|         buf.discard() | ||||
|         throw e | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // For every type used in the interface, we provide helper methods for conveniently | ||||
| // lifting and lowering that type from C-compatible data, and for reading and writing | ||||
| // values of that type in a buffer. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| internal fun String.Companion.lift(rbuf: RustBuffer.ByValue): String { | ||||
|     try { | ||||
|         val byteArr = ByteArray(rbuf.len) | ||||
|         rbuf.asByteBuffer()!!.get(byteArr) | ||||
|         return byteArr.toString(Charsets.UTF_8) | ||||
|     } finally { | ||||
|         RustBuffer.free(rbuf) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal fun String.Companion.read(buf: ByteBuffer): String { | ||||
|     val len = buf.getInt() | ||||
|     val byteArr = ByteArray(len) | ||||
|     buf.get(byteArr) | ||||
|     return byteArr.toString(Charsets.UTF_8) | ||||
| } | ||||
| 
 | ||||
| internal fun String.lower(): RustBuffer.ByValue { | ||||
|     val byteArr = this.toByteArray(Charsets.UTF_8) | ||||
|     // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us | ||||
|     // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. | ||||
|     val rbuf = RustBuffer.alloc(byteArr.size) | ||||
|     rbuf.asByteBuffer()!!.put(byteArr) | ||||
|     return rbuf | ||||
| } | ||||
| 
 | ||||
| internal fun String.write(buf: RustBufferBuilder) { | ||||
|     val byteArr = this.toByteArray(Charsets.UTF_8) | ||||
|     buf.putInt(byteArr.size) | ||||
|     buf.put(byteArr) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @Synchronized | ||||
| fun findLibraryName(componentName: String): String { | ||||
|     val libOverride = System.getProperty("uniffi.component.${componentName}.libraryOverride") | ||||
|     if (libOverride != null) { | ||||
|         return libOverride | ||||
|     } | ||||
|     return "uniffi_bdk" | ||||
| } | ||||
| 
 | ||||
| inline fun <reified Lib : Library> loadIndirect( | ||||
|     componentName: String | ||||
| ): Lib { | ||||
|     return Native.load<Lib>(findLibraryName(componentName), Lib::class.java) | ||||
| } | ||||
| 
 | ||||
| // A JNA Library to expose the extern-C FFI definitions. | ||||
| // This is an implementation detail which will be called internally by the public API. | ||||
| 
 | ||||
| internal interface _UniFFILib : Library { | ||||
|     companion object { | ||||
|         internal val INSTANCE: _UniFFILib by lazy {  | ||||
|             loadIndirect<_UniFFILib>(componentName = "bdk") | ||||
|              | ||||
|              | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun ffi_bdk_ed55_OfflineWallet_object_free(ptr: Pointer, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): Unit | ||||
| 
 | ||||
|     fun bdk_ed55_OfflineWallet_new(descriptor: RustBuffer.ByValue, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): Pointer | ||||
| 
 | ||||
|     fun ffi_bdk_ed55_rustbuffer_alloc(size: Int, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): RustBuffer.ByValue | ||||
| 
 | ||||
|     fun ffi_bdk_ed55_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): RustBuffer.ByValue | ||||
| 
 | ||||
|     fun ffi_bdk_ed55_rustbuffer_free(buf: RustBuffer.ByValue, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): Unit | ||||
| 
 | ||||
|     fun ffi_bdk_ed55_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, | ||||
|     uniffi_out_err: RustCallStatus | ||||
|     ): RustBuffer.ByValue | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| // A handful of classes and functions to support the generated data structures. | ||||
| // This would be a good candidate for isolating in its own ffi-support lib. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Interface implemented by anything that can contain an object reference. | ||||
| // | ||||
| // Such types expose a `destroy()` method that must be called to cleanly | ||||
| // dispose of the contained objects. Failure to call this method may result | ||||
| // in memory leaks. | ||||
| // | ||||
| // The easiest way to ensure this method is called is to use the `.use` | ||||
| // helper method to execute a block and destroy the object at the end. | ||||
| interface Disposable { | ||||
|     fun destroy() | ||||
| } | ||||
| 
 | ||||
| inline fun <T : Disposable?, R> T.use(block: (T) -> R) = | ||||
|     try { | ||||
|         block(this) | ||||
|     } finally { | ||||
|         try { | ||||
|             // N.B. our implementation is on the nullable type `Disposable?`. | ||||
|             this?.destroy() | ||||
|         } catch (e: Throwable) { | ||||
|             // swallow | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| // The base class for all UniFFI Object types. | ||||
| // | ||||
| // This class provides core operations for working with the Rust `Arc<T>` pointer to | ||||
| // the live Rust struct on the other side of the FFI. | ||||
| // | ||||
| // There's some subtlety here, because we have to be careful not to operate on a Rust | ||||
| // struct after it has been dropped, and because we must expose a public API for freeing | ||||
| // the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: | ||||
| // | ||||
| //   * Each `FFIObject` instance holds an opaque pointer to the underlying Rust struct. | ||||
| //     Method calls need to read this pointer from the object's state and pass it in to | ||||
| //     the Rust FFI. | ||||
| // | ||||
| //   * When an `FFIObject` is no longer needed, its pointer should be passed to a | ||||
| //     special destructor function provided by the Rust FFI, which will drop the | ||||
| //     underlying Rust struct. | ||||
| // | ||||
| //   * Given an `FFIObject` instance, calling code is expected to call the special | ||||
| //     `destroy` method in order to free it after use, either by calling it explicitly | ||||
| //     or by using a higher-level helper like the `use` method. Failing to do so will | ||||
| //     leak the underlying Rust struct. | ||||
| // | ||||
| //   * We can't assume that calling code will do the right thing, and must be prepared | ||||
| //     to handle Kotlin method calls executing concurrently with or even after a call to | ||||
| //     `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. | ||||
| // | ||||
| //   * We must never allow Rust code to operate on the underlying Rust struct after | ||||
| //     the destructor has been called, and must never call the destructor more than once. | ||||
| //     Doing so may trigger memory unsafety. | ||||
| // | ||||
| // If we try to implement this with mutual exclusion on access to the pointer, there is the | ||||
| // possibility of a race between a method call and a concurrent call to `destroy`: | ||||
| // | ||||
| //    * Thread A starts a method call, reads the value of the pointer, but is interrupted | ||||
| //      before it can pass the pointer over the FFI to Rust. | ||||
| //    * Thread B calls `destroy` and frees the underlying Rust struct. | ||||
| //    * Thread A resumes, passing the already-read pointer value to Rust and triggering | ||||
| //      a use-after-free. | ||||
| // | ||||
| // One possible solution would be to use a `ReadWriteLock`, with each method call taking | ||||
| // a read lock (and thus allowed to run concurrently) and the special `destroy` method | ||||
| // taking a write lock (and thus blocking on live method calls). However, we aim not to | ||||
| // generate methods with any hidden blocking semantics, and a `destroy` method that might | ||||
| // block if called incorrectly seems to meet that bar. | ||||
| // | ||||
| // So, we achieve our goals by giving each `FFIObject` an associated `AtomicLong` counter to track | ||||
| // the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` | ||||
| // has been called. These are updated according to the following rules: | ||||
| // | ||||
| //    * The initial value of the counter is 1, indicating a live object with no in-flight calls. | ||||
| //      The initial value for the flag is false. | ||||
| // | ||||
| //    * At the start of each method call, we atomically check the counter. | ||||
| //      If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. | ||||
| //      If it is nonzero them we atomically increment it by 1 and proceed with the method call. | ||||
| // | ||||
| //    * At the end of each method call, we atomically decrement and check the counter. | ||||
| //      If it has reached zero then we destroy the underlying Rust struct. | ||||
| // | ||||
| //    * When `destroy` is called, we atomically flip the flag from false to true. | ||||
| //      If the flag was already true we silently fail. | ||||
| //      Otherwise we atomically decrement and check the counter. | ||||
| //      If it has reached zero then we destroy the underlying Rust struct. | ||||
| // | ||||
| // Astute readers may observe that this all sounds very similar to the way that Rust's `Arc<T>` works, | ||||
| // and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. | ||||
| // | ||||
| // The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been | ||||
| // called *and* all in-flight method calls have completed, avoiding violating any of the expectations | ||||
| // of the underlying Rust code. | ||||
| // | ||||
| // In the future we may be able to replace some of this with automatic finalization logic, such as using | ||||
| // the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is | ||||
| // invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also | ||||
| // possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1], | ||||
| // so there would still be some complexity here). | ||||
| // | ||||
| // Sigh...all of this for want of a robust finalization mechanism. | ||||
| // | ||||
| // [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 | ||||
| // | ||||
| abstract class FFIObject( | ||||
|     protected val pointer: Pointer | ||||
| ): Disposable, AutoCloseable { | ||||
| 
 | ||||
|     val wasDestroyed = AtomicBoolean(false) | ||||
|     val callCounter = AtomicLong(1) | ||||
| 
 | ||||
|     open protected fun freeRustArcPtr() { | ||||
|         // To be overridden in subclasses. | ||||
|     } | ||||
| 
 | ||||
|     override fun destroy() { | ||||
|         // Only allow a single call to this method. | ||||
|         // TODO: maybe we should log a warning if called more than once? | ||||
|         if (this.wasDestroyed.compareAndSet(false, true)) { | ||||
|             // This decrement always matches the initial count of 1 given at creation time. | ||||
|             if (this.callCounter.decrementAndGet() == 0L) { | ||||
|                 this.freeRustArcPtr() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Synchronized | ||||
|     override fun close() { | ||||
|         this.destroy() | ||||
|     } | ||||
| 
 | ||||
|     internal inline fun <R> callWithPointer(block: (ptr: Pointer) -> R): R { | ||||
|         // Check and increment the call counter, to keep the object alive. | ||||
|         // This needs a compare-and-set retry loop in case of concurrent updates. | ||||
|         do { | ||||
|             val c = this.callCounter.get() | ||||
|             if (c == 0L) { | ||||
|                 throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") | ||||
|             } | ||||
|             if (c == Long.MAX_VALUE) { | ||||
|                 throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") | ||||
|             } | ||||
|         } while (! this.callCounter.compareAndSet(c, c + 1L)) | ||||
|         // Now we can safely do the method call without the pointer being freed concurrently. | ||||
|         try { | ||||
|             return block(this.pointer) | ||||
|         } finally { | ||||
|             // This decrement aways matches the increment we performed above. | ||||
|             if (this.callCounter.decrementAndGet() == 0L) { | ||||
|                 this.freeRustArcPtr() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Public interface members begin here. | ||||
| // Public facing enums | ||||
| // Error definitions | ||||
| @Structure.FieldOrder("code", "error_buf") | ||||
| internal open class RustCallStatus : Structure() { | ||||
|     @JvmField var code: Int = 0 | ||||
|     @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() | ||||
| 
 | ||||
|     fun isSuccess(): Boolean { | ||||
|         return code == 0 | ||||
|     } | ||||
| 
 | ||||
|     fun isError(): Boolean { | ||||
|         return code == 1 | ||||
|     } | ||||
| 
 | ||||
|     fun isPanic(): Boolean { | ||||
|         return code == 2 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class InternalException(message: String) : Exception(message) | ||||
| 
 | ||||
| // Each top-level error class has a companion object that can lift the error from the call status's rust buffer | ||||
| interface CallStatusErrorHandler<E> { | ||||
|     fun lift(error_buf: RustBuffer.ByValue): E; | ||||
| } | ||||
| 
 | ||||
| // Helpers for calling Rust | ||||
| // In practice we usually need to be synchronized to call this safely, so it doesn't | ||||
| // synchronize itself | ||||
| 
 | ||||
| // Call a rust function that returns a Result<>.  Pass in the Error class companion that corresponds to the Err | ||||
| private inline fun <U, E: Exception> rustCallWithError(errorHandler: CallStatusErrorHandler<E>, callback: (RustCallStatus) -> U): U { | ||||
|     var status = RustCallStatus(); | ||||
|     val return_value = callback(status) | ||||
|     if (status.isSuccess()) { | ||||
|         return return_value | ||||
|     } else if (status.isError()) { | ||||
|         throw errorHandler.lift(status.error_buf) | ||||
|     } else if (status.isPanic()) { | ||||
|         // when the rust code sees a panic, it tries to construct a rustbuffer | ||||
|         // with the message.  but if that code panics, then it just sends back | ||||
|         // an empty buffer. | ||||
|         if (status.error_buf.len > 0) { | ||||
|             throw InternalException(String.lift(status.error_buf)) | ||||
|         } else { | ||||
|             throw InternalException("Rust panic") | ||||
|         } | ||||
|     } else { | ||||
|         throw InternalException("Unknown rust call status: $status.code") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR | ||||
| object NullCallStatusErrorHandler: CallStatusErrorHandler<InternalException> { | ||||
|     override fun lift(error_buf: RustBuffer.ByValue): InternalException { | ||||
|         RustBuffer.free(error_buf) | ||||
|         return InternalException("Unexpected CALL_ERROR") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Call a rust function that returns a plain value | ||||
| private inline fun <U> rustCall(callback: (RustCallStatus) -> U): U { | ||||
|     return rustCallWithError(NullCallStatusErrorHandler, callback); | ||||
| } | ||||
| 
 | ||||
| // Public facing records | ||||
| 
 | ||||
| // Namespace functions | ||||
| 
 | ||||
| 
 | ||||
| // Objects | ||||
| 
 | ||||
| 
 | ||||
| public interface OfflineWalletInterface { | ||||
|      | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class OfflineWallet( | ||||
|     pointer: Pointer | ||||
| ) : FFIObject(pointer), OfflineWalletInterface { | ||||
|     constructor(descriptor: String ) : | ||||
|         this( | ||||
|     rustCall() { status -> | ||||
|     _UniFFILib.INSTANCE.bdk_ed55_OfflineWallet_new(descriptor.lower() ,status) | ||||
| }) | ||||
| 
 | ||||
|     /** | ||||
|      * Disconnect the object from the underlying Rust object. | ||||
|      *  | ||||
|      * It can be called more than once, but once called, interacting with the object | ||||
|      * causes an `IllegalStateException`. | ||||
|      *  | ||||
|      * Clients **must** call this method once done with the object, or cause a memory leak. | ||||
|      */ | ||||
|     override protected fun freeRustArcPtr() { | ||||
|         rustCall() { status -> | ||||
|             _UniFFILib.INSTANCE.ffi_bdk_ed55_OfflineWallet_object_free(this.pointer, status) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal fun lower(): Pointer = callWithPointer { it } | ||||
| 
 | ||||
|     internal fun write(buf: RustBufferBuilder) { | ||||
|         // The Rust code always expects pointers written as 8 bytes, | ||||
|         // and will fail to compile if they don't fit. | ||||
|         buf.putLong(Pointer.nativeValue(this.lower())) | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|     companion object { | ||||
|         internal fun lift(ptr: Pointer): OfflineWallet { | ||||
|             return OfflineWallet(ptr) | ||||
|         } | ||||
| 
 | ||||
|         internal fun read(buf: ByteBuffer): OfflineWallet { | ||||
|             // The Rust code always writes pointers as 8 bytes, and will | ||||
|             // fail to compile if they don't fit. | ||||
|             return OfflineWallet.lift(Pointer(buf.getLong())) | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Callback Interfaces | ||||
| 
 | ||||
| 
 | ||||
| @ -1,106 +0,0 @@ | ||||
| use ::safer_ffi::prelude::*; | ||||
| use bdk::blockchain::{AnyBlockchainConfig, ElectrumBlockchainConfig}; | ||||
| use safer_ffi::boxed::Box; | ||||
| use safer_ffi::char_p::char_p_ref; | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[ReprC::opaque] | ||||
| #[derive(Debug)] | ||||
| pub struct BlockchainConfig { | ||||
|     pub raw: AnyBlockchainConfig, | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn new_electrum_config( | ||||
|     url: char_p_ref, | ||||
|     socks5: Option<char_p_ref>, | ||||
|     retry: i16, | ||||
|     timeout: i16, | ||||
|     stop_gap: usize, | ||||
| ) -> Box<BlockchainConfig> { | ||||
|     let url = url.to_string(); | ||||
|     let socks5 = socks5.map(|s| s.to_string()); | ||||
|     let retry = short_to_u8(retry); | ||||
|     let timeout = short_to_optional_u8(timeout); | ||||
| 
 | ||||
|     let electrum_config = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { | ||||
|         url, | ||||
|         socks5, | ||||
|         retry, | ||||
|         timeout, | ||||
|         stop_gap, | ||||
|     }); | ||||
|     Box::new(BlockchainConfig { | ||||
|         raw: electrum_config, | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_blockchain_config(blockchain_config: Box<BlockchainConfig>) { | ||||
|     drop(blockchain_config); | ||||
| } | ||||
| 
 | ||||
| // TODO compact_filter rocksdb not compiling on android, switch to sqlite?
 | ||||
| //#[derive_ReprC]
 | ||||
| //#[repr(C)]
 | ||||
| //#[derive(Debug)]
 | ||||
| //pub struct BitcoinPeerConfig {
 | ||||
| //    pub address: char_p_boxed,
 | ||||
| //    pub socks5: Option<char_p_boxed>,
 | ||||
| //    pub socks5_credentials: Option<Box<Tuple2<char_p_boxed, char_p_boxed>>>,
 | ||||
| //}
 | ||||
| //
 | ||||
| //impl From<&BitcoinPeerConfig> for BdkBitcoinPeerConfig {
 | ||||
| //    fn from(config: &BitcoinPeerConfig) -> Self {
 | ||||
| //        let address = config.address.to_string();
 | ||||
| //        let socks5 = config.socks5.as_ref().map(|p| p.to_string());
 | ||||
| //        let socks5_credentials = config
 | ||||
| //            .socks5_credentials.as_ref()
 | ||||
| //            .map(|c| (c._0.to_string(), c._1.to_string()));
 | ||||
| //
 | ||||
| //        BdkBitcoinPeerConfig {
 | ||||
| //            address,
 | ||||
| //            socks5: socks5,
 | ||||
| //            socks5_credentials: socks5_credentials,
 | ||||
| //        }
 | ||||
| //    }
 | ||||
| //}
 | ||||
| //
 | ||||
| //
 | ||||
| //#[ffi_export]
 | ||||
| //fn new_compact_filters_config<'lt>(
 | ||||
| //    peers: c_slice::Ref<'lt, BitcoinPeerConfig>,
 | ||||
| //    network: char_p_ref,
 | ||||
| //    storage_dir: char_p_ref,
 | ||||
| //    skip_blocks: usize,
 | ||||
| //) -> Box<BlockchainConfig> {
 | ||||
| //    let peers = peers.iter().map(|p| p.into()).collect();
 | ||||
| //    let network = Network::from_str(network.to_str()).unwrap();
 | ||||
| //    let storage_dir = storage_dir.to_string();
 | ||||
| //    let skip_blocks = Some(skip_blocks);
 | ||||
| //    let cf_config = AnyBlockchainConfig::CompactFilters(CompactFiltersBlockchainConfig {
 | ||||
| //        peers,
 | ||||
| //        network,
 | ||||
| //        storage_dir,
 | ||||
| //        skip_blocks,
 | ||||
| //    });
 | ||||
| //    Box::new(BlockchainConfig { raw: cf_config })
 | ||||
| //}
 | ||||
| 
 | ||||
| // utility functions
 | ||||
| 
 | ||||
| fn short_to_optional_u8(short: i16) -> Option<u8> { | ||||
|     if short < 0 { | ||||
|         None | ||||
|     } else { | ||||
|         Some(short_to_u8(short)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn short_to_u8(short: i16) -> u8 { | ||||
|     if short < 0 { | ||||
|         u8::MIN | ||||
|     } else { | ||||
|         u8::try_from(short).unwrap_or(u8::MAX) | ||||
|     } | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| use ::safer_ffi::prelude::*; | ||||
| use bdk::database::any::SledDbConfiguration; | ||||
| use bdk::database::AnyDatabaseConfig; | ||||
| use safer_ffi::boxed::Box; | ||||
| use safer_ffi::char_p::char_p_ref; | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[ReprC::opaque] | ||||
| #[derive(Debug)] | ||||
| pub struct DatabaseConfig { | ||||
|     pub raw: AnyDatabaseConfig, | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn new_memory_config() -> Box<DatabaseConfig> { | ||||
|     let memory_config = AnyDatabaseConfig::Memory(()); | ||||
|     Box::new(DatabaseConfig { raw: memory_config }) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn new_sled_config(path: char_p_ref, tree_name: char_p_ref) -> Box<DatabaseConfig> { | ||||
|     let path = path.to_string(); | ||||
|     let tree_name = tree_name.to_string(); | ||||
| 
 | ||||
|     let sled_config = AnyDatabaseConfig::Sled(SledDbConfiguration { path, tree_name }); | ||||
|     Box::new(DatabaseConfig { raw: sled_config }) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_database_config(database_config: Box<DatabaseConfig>) { | ||||
|     drop(database_config); | ||||
| } | ||||
| @ -1,175 +0,0 @@ | ||||
| use std::convert::TryFrom; | ||||
| use std::ffi::CString; | ||||
| 
 | ||||
| use ::safer_ffi::prelude::*; | ||||
| use bdk::blockchain::{log_progress, AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain}; | ||||
| use bdk::database::{AnyDatabase, AnyDatabaseConfig, ConfigurableDatabase}; | ||||
| use bdk::wallet::AddressIndex::New; | ||||
| use bdk::{Error, Wallet}; | ||||
| use safer_ffi::boxed::Box; | ||||
| use safer_ffi::char_p::{char_p_boxed, char_p_ref}; | ||||
| 
 | ||||
| use blockchain::BlockchainConfig; | ||||
| use database::DatabaseConfig; | ||||
| 
 | ||||
| use crate::error::FfiError; | ||||
| use crate::types::{FfiResult, FfiResultVoid}; | ||||
| use crate::wallet::transaction::{LocalUtxo, TransactionDetails}; | ||||
| use bdk::bitcoin::Network; | ||||
| use std::str::FromStr; | ||||
| 
 | ||||
| mod blockchain; | ||||
| mod database; | ||||
| mod transaction; | ||||
| 
 | ||||
| // create a new wallet
 | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[ReprC::opaque] | ||||
| pub struct OpaqueWallet { | ||||
|     raw: Wallet<AnyBlockchain, AnyDatabase>, | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn new_wallet_result( | ||||
|     descriptor: char_p_ref, | ||||
|     change_descriptor: Option<char_p_ref>, | ||||
|     network: char_p_ref, | ||||
|     blockchain_config: &BlockchainConfig, | ||||
|     database_config: &DatabaseConfig, | ||||
| ) -> FfiResult<Option<Box<OpaqueWallet>>> { | ||||
|     let descriptor = descriptor.to_string(); | ||||
|     let change_descriptor = change_descriptor.map(|s| s.to_string()); | ||||
|     let net = Network::from_str(network.to_str()).expect("Network name"); | ||||
|     let bc_config = &blockchain_config.raw; | ||||
|     let db_config = &database_config.raw; | ||||
|     let wallet_result = new_wallet(descriptor, change_descriptor, net, bc_config, db_config); | ||||
| 
 | ||||
|     match wallet_result { | ||||
|         Ok(w) => FfiResult { | ||||
|             ok: Some(Box::new(OpaqueWallet { raw: w })), | ||||
|             err: FfiError::None, | ||||
|         }, | ||||
|         Err(e) => FfiResult { | ||||
|             ok: None, | ||||
|             err: FfiError::from(&e), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn new_wallet( | ||||
|     descriptor: String, | ||||
|     change_descriptor: Option<String>, | ||||
|     network: Network, | ||||
|     blockchain_config: &AnyBlockchainConfig, | ||||
|     database_config: &AnyDatabaseConfig, | ||||
| ) -> Result<Wallet<AnyBlockchain, AnyDatabase>, Error> { | ||||
|     let client = AnyBlockchain::from_config(blockchain_config)?; | ||||
|     let database = AnyDatabase::from_config(database_config)?; | ||||
| 
 | ||||
|     let descriptor: &str = descriptor.as_str(); | ||||
|     let change_descriptor: Option<&str> = change_descriptor.as_deref(); | ||||
| 
 | ||||
|     Wallet::new(descriptor, change_descriptor, network, database, client) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_wallet_result(wallet_result: FfiResult<Option<Box<OpaqueWallet>>>) { | ||||
|     drop(wallet_result); | ||||
| } | ||||
| 
 | ||||
| // wallet operations
 | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn sync_wallet(opaque_wallet: &OpaqueWallet) -> FfiResultVoid { | ||||
|     let int_result = opaque_wallet.raw.sync(log_progress(), Some(100)); | ||||
|     match int_result { | ||||
|         Ok(_v) => FfiResultVoid { | ||||
|             err: FfiError::None, | ||||
|         }, | ||||
|         Err(e) => FfiResultVoid { | ||||
|             err: FfiError::from(&e), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn new_address(opaque_wallet: &OpaqueWallet) -> FfiResult<char_p_boxed> { | ||||
|     let new_address = opaque_wallet.raw.get_address(New); | ||||
|     let string_result = new_address.map(|a| a.to_string()); | ||||
|     match string_result { | ||||
|         Ok(a) => FfiResult { | ||||
|             ok: char_p_boxed::try_from(a).unwrap(), | ||||
|             err: FfiError::None, | ||||
|         }, | ||||
|         Err(e) => FfiResult { | ||||
|             ok: char_p_boxed::from(CString::default()), | ||||
|             err: FfiError::from(&e), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn list_unspent(opaque_wallet: &OpaqueWallet) -> FfiResult<repr_c::Vec<LocalUtxo>> { | ||||
|     let unspent_result = opaque_wallet.raw.list_unspent(); | ||||
| 
 | ||||
|     match unspent_result { | ||||
|         Ok(v) => FfiResult { | ||||
|             ok: { | ||||
|                 let ve: Vec<LocalUtxo> = v.iter().map(|lu| LocalUtxo::from(lu)).collect(); | ||||
|                 repr_c::Vec::from(ve) | ||||
|             }, | ||||
|             err: FfiError::None, | ||||
|         }, | ||||
|         Err(e) => FfiResult { | ||||
|             ok: repr_c::Vec::EMPTY, | ||||
|             err: FfiError::from(&e), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_veclocalutxo_result(unspent_result: FfiResult<repr_c::Vec<LocalUtxo>>) { | ||||
|     drop(unspent_result) | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn balance(opaque_wallet: &OpaqueWallet) -> FfiResult<u64> { | ||||
|     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), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn list_transactions(opaque_wallet: &OpaqueWallet) -> FfiResult<repr_c::Vec<TransactionDetails>> { | ||||
|     let transactions_result = opaque_wallet.raw.list_transactions(false); | ||||
| 
 | ||||
|     match transactions_result { | ||||
|         Ok(v) => FfiResult { | ||||
|             ok: { | ||||
|                 let ve: Vec<TransactionDetails> = | ||||
|                     v.iter().map(|t| TransactionDetails::from(t)).collect(); | ||||
|                 repr_c::Vec::from(ve) | ||||
|             }, | ||||
|             err: FfiError::None, | ||||
|         }, | ||||
|         Err(e) => FfiResult { | ||||
|             ok: repr_c::Vec::EMPTY, | ||||
|             err: FfiError::from(&e), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[ffi_export] | ||||
| fn free_vectxdetails_result(txdetails_result: FfiResult<repr_c::Vec<TransactionDetails>>) { | ||||
|     drop(txdetails_result) | ||||
| } | ||||
| @ -1,124 +0,0 @@ | ||||
| use std::convert::TryFrom; | ||||
| 
 | ||||
| use ::safer_ffi::prelude::*; | ||||
| use safer_ffi::char_p::char_p_boxed; | ||||
| 
 | ||||
| // Non-opaque returned values
 | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct TransactionDetails { | ||||
|     // TODO Optional transaction
 | ||||
|     // pub transaction: Option<Transaction>,
 | ||||
|     /// Transaction id
 | ||||
|     pub txid: char_p_boxed, | ||||
|     /// Received value (sats)
 | ||||
|     pub received: u64, | ||||
|     /// Sent value (sats)
 | ||||
|     pub sent: u64, | ||||
|     /// Fee value (sats) if known, -1 if unknown, based on backend
 | ||||
|     pub fee: i64, | ||||
|     /// true if confirmed
 | ||||
|     pub is_confirmed: bool, | ||||
|     /// Confirmed in block height
 | ||||
|     pub confirmation_time: ConfirmationTime, | ||||
|     /// Whether the tx has been verified against the consensus rules
 | ||||
|     pub verified: bool, | ||||
| } | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct ConfirmationTime { | ||||
|     /// confirmation block height, 0 if is_confirmed is false
 | ||||
|     pub height: u32, | ||||
|     /// confirmation block timestamp, 0 if is_confirmed is false
 | ||||
|     pub timestamp: u64, | ||||
| } | ||||
| 
 | ||||
| impl From<&bdk::TransactionDetails> for TransactionDetails { | ||||
|     fn from(op: &bdk::TransactionDetails) -> Self { | ||||
|         let fee = op.fee.map(|f| i64::try_from(f).unwrap()).unwrap_or(-1); | ||||
|         let confirmation_time = op | ||||
|             .confirmation_time | ||||
|             .as_ref() | ||||
|             .map(|c| ConfirmationTime { | ||||
|                 height: c.height, | ||||
|                 timestamp: c.timestamp, | ||||
|             }) | ||||
|             .unwrap_or(ConfirmationTime { | ||||
|                 height: 0, | ||||
|                 timestamp: 0, | ||||
|             }); | ||||
|         TransactionDetails { | ||||
|             txid: char_p_boxed::try_from(op.txid.to_string()).unwrap(), | ||||
|             received: op.received, | ||||
|             sent: op.sent, | ||||
|             fee, | ||||
|             is_confirmed: op.confirmation_time.is_some(), | ||||
|             confirmation_time, | ||||
|             verified: op.verified, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct OutPoint { | ||||
|     /// The referenced transaction's txid, as hex string
 | ||||
|     pub txid: char_p_boxed, | ||||
|     /// The index of the referenced output in its transaction's vout
 | ||||
|     pub vout: u32, | ||||
| } | ||||
| 
 | ||||
| impl From<&bdk::bitcoin::OutPoint> for OutPoint { | ||||
|     fn from(op: &bdk::bitcoin::OutPoint) -> Self { | ||||
|         OutPoint { | ||||
|             txid: char_p_boxed::try_from(op.txid.to_string()).unwrap(), | ||||
|             vout: op.vout, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct TxOut { | ||||
|     /// The value of the output, in satoshis
 | ||||
|     pub value: u64, | ||||
|     /// The script which must satisfy for the output to be spent, as hex string
 | ||||
|     pub script_pubkey: char_p_boxed, | ||||
| } | ||||
| 
 | ||||
| impl From<&bdk::bitcoin::TxOut> for TxOut { | ||||
|     fn from(to: &bdk::bitcoin::TxOut) -> Self { | ||||
|         TxOut { | ||||
|             value: to.value, | ||||
|             script_pubkey: char_p_boxed::try_from(to.script_pubkey.to_string()).unwrap(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive_ReprC] | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct LocalUtxo { | ||||
|     /// Reference to a transaction output
 | ||||
|     pub outpoint: OutPoint, | ||||
|     /// Transaction output
 | ||||
|     pub txout: TxOut, | ||||
|     /// Type of keychain, as short 0 for "external" or 1 for "internal"
 | ||||
|     pub keychain: u16, | ||||
| } | ||||
| 
 | ||||
| impl From<&bdk::LocalUtxo> for LocalUtxo { | ||||
|     fn from(lu: &bdk::LocalUtxo) -> Self { | ||||
|         LocalUtxo { | ||||
|             outpoint: OutPoint::from(&lu.outpoint), | ||||
|             txout: TxOut::from(&lu.txout), | ||||
|             keychain: lu.keychain as u16, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| # Project-wide Gradle settings. | ||||
| # IDE (e.g. Android Studio) users: | ||||
| # Gradle settings configured through the IDE *will override* | ||||
| # any settings specified in this file. | ||||
| # For more details on how to configure your build environment visit | ||||
| # http://www.gradle.org/docs/current/userguide/build_environment.html | ||||
| # Specifies the JVM arguments used for the daemon process. | ||||
| # The setting is particularly useful for tweaking memory settings. | ||||
| org.gradle.jvmargs=-Xmx1536m | ||||
| # When configured, Gradle will run in incubating parallel mode. | ||||
| # This option should only be used with decoupled projects. More details, visit | ||||
| # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects | ||||
| # org.gradle.parallel=true | ||||
| # AndroidX package structure to make it clearer which packages are bundled with the | ||||
| # Android operating system, and which are packaged with your app's APK | ||||
| # https://developer.android.com/topic/libraries/support-library/androidx-rn | ||||
| android.useAndroidX=true | ||||
| # Automatically convert third-party libraries to use AndroidX | ||||
| android.enableJetifier=true | ||||
| # Kotlin code style for this project: "official" or "obsolete": | ||||
| kotlin.code.style=official | ||||
| jna.debug_load=true | ||||
| jna.debug_load.jna=true | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user