[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 | bdk_ffi_test | ||||||
| local.properties | local.properties | ||||||
| *.log | *.log | ||||||
| targets/kotlin/testdb | *.dylib | ||||||
| @ -1,7 +1,7 @@ | |||||||
| [package] | [package] | ||||||
| name = "uniffi_bdk" | name = "uniffi_bdk" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| authors = ["Steve Myers <steve@notmandatory.org>"] | authors = ["Steve Myers <steve@notmandatory.org>", ""] | ||||||
| edition = "2018" | edition = "2018" | ||||||
| 
 | 
 | ||||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | # 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  | 1. cargo install uniffi_bindgen  | ||||||
| 2. cargo build | 2. cargo build | ||||||
| 3. uniffi-bindgen generate --no-format --out-dir targets/kotlin/src/main/kotlin src/bdk.udl --language kotlin | 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 targets/kotlin/src/main/resources/darwin-x86-64 | 4. cp target/debug/libuniffi_bdk.dylib bindings/bdk-kotlin/src/main/resources/darwin-x86-64 | ||||||
| 5. gradle build -Djna.debug_load=true -Djna.debug_load.jna | 5. cd bindings/bdk-kotlin; gradle build -Djna.debug_load=true -Djna.debug_load.jna | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Setup Android build environment | 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 | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | 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 | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
| @ -19,7 +19,7 @@ class LibTest { | |||||||
|         val address = wallet.getNewAddress() |         val address = wallet.getNewAddress() | ||||||
|         println("address:" + address) |         println("address:" + address) | ||||||
|         assertNotNull(address) |         assertNotNull(address) | ||||||
|         // log.debug("address created from kotlin: $address") |         // log.debug("address created from bdk-kotlin: $address") | ||||||
|         assertEquals(address, "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs") |         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 |   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() { | copy_lib_kotlin() { | ||||||
|   echo -n "Copy " |   echo -n "Copy " | ||||||
|   case $OS in |   case $OS in | ||||||
|     "Darwin") |     "Darwin") | ||||||
|       echo -n "darwin " |       echo -n "darwin " | ||||||
|       mkdir -p 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-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") |     "Linux") | ||||||
|       echo -n "linux " |       echo -n "linux " | ||||||
|       mkdir -p 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-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 |   esac | ||||||
|   echo "libs to kotlin sub-project" |   echo "libs to kotlin sub-project" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ## bdk-kotlin jar | ## bdk-bdk-kotlin jar | ||||||
| build_kotlin() { | build_kotlin() { | ||||||
|   (cd bdk-kotlin && ./gradlew :jvm:build && ./gradlew :jvm:publishToMavenLocal) |   (cd bdk-bdk-kotlin && ./gradlew :jvm:build && ./gradlew :jvm:publishToMavenLocal) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ## rust android | ## 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 |   # 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}" |   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 |   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 |       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 |   fi | ||||||
|   if echo $BUILD_TARGETS | grep "x86_64"; then |   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 |       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 |   fi | ||||||
|   if echo $BUILD_TARGETS | grep "armv7"; then |   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 |       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 |   fi | ||||||
|   if echo $BUILD_TARGETS | grep "i686"; then |   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 |       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 |   fi | ||||||
| 
 | 
 | ||||||
|   # bdk-kotlin aar |   # bdk-bdk-kotlin aar | ||||||
|   (cd bdk-kotlin && ./gradlew :android:build && ./gradlew :android:publishToMavenLocal) |   (cd bdk-bdk-kotlin && ./gradlew :android:build && ./gradlew :android:publishToMavenLocal) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| OS=$(uname) | 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