From aa13e113fa50db6fb4dc455193d477f23f79449b Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Sat, 19 Mar 2022 11:01:18 -0400 Subject: [PATCH] Add required files for API docs 0.5.1 --- android/Module.md | 2 +- docs-0.4.0.patch | 2088 --------------------------------------------- docs.patch | 804 +++++++++++++++++ jvm/Module.md | 2 +- 4 files changed, 806 insertions(+), 2090 deletions(-) delete mode 100644 docs-0.4.0.patch create mode 100644 docs.patch diff --git a/android/Module.md b/android/Module.md index 036a5ad..f3e8d26 100644 --- a/android/Module.md +++ b/android/Module.md @@ -1,4 +1,4 @@ # Module bdk-android -The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android. Current version: `0.4.0`. +The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android. Current version: `0.5.1`. # Package org.bitcoindevkit diff --git a/docs-0.4.0.patch b/docs-0.4.0.patch deleted file mode 100644 index 3370a50..0000000 --- a/docs-0.4.0.patch +++ /dev/null @@ -1,2088 +0,0 @@ -*** ./temp-without-docs/org/bitcoindevkit/bdk.kt 2022-03-15 22:56:50.000000000 -0400 ---- ./android/src/main/kotlin/org/bitcoindevkit/bdk.kt 2022-03-15 23:00:28.000000000 -0400 -*************** -*** 1,181 **** ---- 1,194 ---- - // 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 org.bitcoindevkit; - - // 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 com.sun.jna.ptr.ByReference - import java.nio.ByteBuffer - import java.nio.ByteOrder - import java.util.concurrent.atomic.AtomicBoolean - import java.util.concurrent.atomic.AtomicLong - import java.util.concurrent.locks.ReentrantLock - import kotlin.concurrent.withLock - - // The Rust Buffer and 3 templated methods (alloc, free, reserve). - // 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. - -+ -+ /** -+ * @suppress -+ */ - @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_6983_rustbuffer_alloc(size, status).also { - if(it.data == null) { - throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") - } - } - } - - internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_6983_rustbuffer_free(buf, status) - } - - internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_6983_rustbuffer_reserve(buf, additional, status) - } - } - - @Suppress("TooGenericExceptionThrown") - fun asByteBuffer() = - this.data?.getByteBuffer(0, this.len.toLong())?.also { - it.order(ByteOrder.BIG_ENDIAN) - } - } - - /** - * The equivalent of the `*mut RustBuffer` type. - * Required for callbacks taking in an out pointer. - * - * Size is the sum of all values in the struct. - */ -+ /** -+ * @suppress -+ */ - class RustBufferByReference : ByReference(16) { - /** - * Set the pointed-to `RustBuffer` to the given value. - */ - fun setValue(value: RustBuffer.ByValue) { - // NOTE: The offsets are as they are in the C-like struct. - val pointer = getPointer() - pointer.setInt(0, value.capacity) - pointer.setInt(4, value.len) - pointer.setPointer(8, value.data) - } - } - - // 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. - -+ /** -+ * @suppress -+ */ - @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. - -+ /** -+ * @suppress -+ */ - 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() { - if(this.rbuf.data != null) { - // Free the current `RustBuffer` - RustBuffer.free(this.rbuf) - // Replace it with an empty RustBuffer. - this.setRustBuffer(RustBuffer.ByValue()) - } - } - - 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 -> -*************** -*** 216,934 **** - } - } - - internal fun 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 - } - } - - // 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. - // Error runtime. - @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 { - 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 rustCallWithError(errorHandler: CallStatusErrorHandler, 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 { - 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 rustCall(callback: (RustCallStatus) -> U): U { - return rustCallWithError(NullCallStatusErrorHandler, callback); - } - - // Contains loading, initialization code, - // and the FFI Function declarations in a com.sun.jna.Library. - @Synchronized - private fun findLibraryName(componentName: String): String { - val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") - if (libOverride != null) { - return libOverride - } - return "bdkffi" - } - - private inline fun loadIndirect( - componentName: String - ): Lib { - return Native.load(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") - .also { lib: _UniFFILib -> - FfiConverterCallbackInterfaceBdkProgress.register(lib) - } - - } - } - - fun ffi_bdk_6983_Wallet_object_free(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_new(descriptor: RustBuffer.ByValue,change_descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_Wallet_get_new_address(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_last_unused_address(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_balance(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Long - - fun bdk_6983_Wallet_sign(ptr: Pointer,psbt: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_get_transactions(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_network(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_broadcast(ptr: Pointer,psbt: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long,fee_rate: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_PartiallySignedBitcoinTransaction_deserialize(psbt_base64: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_PartiallySignedBitcoinTransaction_serialize(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_BdkProgress_init_callback(callback_stub: ForeignCallback, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_generate_extended_key(network: RustBuffer.ByValue,word_count: RustBuffer.ByValue,password: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_restore_extended_key(network: RustBuffer.ByValue,mnemonic: RustBuffer.ByValue,password: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_alloc(size: Int, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_free(buf: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Unit - - fun ffi_bdk_6983_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - - } - - // Public interface members begin here. - - // 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() - companion object { - fun destroy(vararg args: Any?) { - args.filterIsInstance() - .forEach(Disposable::destroy) - } - } - } - - inline fun 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` 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` 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 { - - private val wasDestroyed = AtomicBoolean(false) - private 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 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() - } - } - } - } - internal typealias Handle = Long - internal class ConcurrentHandleMap( - private val leftMap: MutableMap = mutableMapOf(), - private val rightMap: MutableMap = mutableMapOf() - ) { - private val lock = java.util.concurrent.locks.ReentrantLock() - private val currentHandle = AtomicLong(0L) - private val stride = 1L - - fun insert(obj: T): Handle = - lock.withLock { - rightMap[obj] ?: - currentHandle.getAndAdd(stride) - .also { handle -> - leftMap[handle] = obj - rightMap[obj] = handle - } - } - - fun get(handle: Handle) = lock.withLock { - leftMap[handle] - } - - fun delete(handle: Handle) { - this.remove(handle) - } - - fun remove(handle: Handle): T? = - lock.withLock { - leftMap.remove(handle)?.let { obj -> - rightMap.remove(obj) - obj - } - } - } - - interface ForeignCallback : com.sun.jna.Callback { - public fun invoke(handle: Handle, method: Int, args: RustBuffer.ByValue, outBuf: RustBufferByReference): Int - } - - // Magic number for the Rust proxy to call using the same mechanism as every other method, - // to free the callback once it's dropped by Rust. - internal const val IDX_CALLBACK_FREE = 0 - - internal abstract class FfiConverterCallbackInterface( - protected val foreignCallback: ForeignCallback - ) { - val handleMap = ConcurrentHandleMap() - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - abstract fun register(lib: _UniFFILib) - - fun drop(handle: Handle): RustBuffer.ByValue { - return handleMap.remove(handle).let { RustBuffer.ByValue() } - } - - fun lift(n: Handle) = handleMap.get(n) - - fun read(buf: ByteBuffer) = lift(buf.getLong()) - - fun lower(v: CallbackInterface) = - handleMap.insert(v).also { - assert(handleMap.get(it) === v) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." } - } - - fun write(v: CallbackInterface, buf: RustBufferBuilder) = - buf.putLong(lower(v)) - } - - - - enum class Network { - BITCOIN,TESTNET,SIGNET,REGTEST; - - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): Network { - return liftFromRustBuffer(rbuf) { buf -> Network.read(buf) } - } - - internal fun read(buf: ByteBuffer) = - try { values()[buf.getInt() - 1] } - catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - buf.putInt(this.ordinal + 1) - } - } - - - - - - - - sealed class DatabaseConfig { - object Memory : DatabaseConfig() - - data class Sled( - val config: SledDbConfiguration - ) : DatabaseConfig() - - data class Sqlite( - val config: SqliteDbConfiguration - ) : DatabaseConfig() -- - - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): DatabaseConfig { - return liftFromRustBuffer(rbuf) { buf -> DatabaseConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): DatabaseConfig { - return when(buf.getInt()) { - 1 -> DatabaseConfig.Memory - 2 -> DatabaseConfig.Sled( - SledDbConfiguration.read(buf) - ) - 3 -> DatabaseConfig.Sqlite( - SqliteDbConfiguration.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is DatabaseConfig.Memory -> { - buf.putInt(1) - - } - is DatabaseConfig.Sled -> { - buf.putInt(2) - this.config.write(buf) - - } - is DatabaseConfig.Sqlite -> { - buf.putInt(3) - this.config.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - - - sealed class Transaction { - - data class Unconfirmed( - val details: TransactionDetails - ) : Transaction() - - data class Confirmed( - val details: TransactionDetails, - val confirmation: BlockTime - ) : Transaction() - - - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): Transaction { - return liftFromRustBuffer(rbuf) { buf -> Transaction.read(buf) } - } - - internal fun read(buf: ByteBuffer): Transaction { - return when(buf.getInt()) { - 1 -> Transaction.Unconfirmed( - TransactionDetails.read(buf) - ) - 2 -> Transaction.Confirmed( - TransactionDetails.read(buf), - BlockTime.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is Transaction.Unconfirmed -> { - buf.putInt(1) - this.details.write(buf) - - } - is Transaction.Confirmed -> { - buf.putInt(2) - this.details.write(buf) - this.confirmation.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - - - sealed class BlockchainConfig { - - data class Electrum( - val config: ElectrumConfig - ) : BlockchainConfig() - - data class Esplora( - val config: EsploraConfig - ) : BlockchainConfig() - - - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): BlockchainConfig { - return liftFromRustBuffer(rbuf) { buf -> BlockchainConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): BlockchainConfig { - return when(buf.getInt()) { - 1 -> BlockchainConfig.Electrum( - ElectrumConfig.read(buf) - ) - 2 -> BlockchainConfig.Esplora( - EsploraConfig.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is BlockchainConfig.Electrum -> { - buf.putInt(1) - this.config.write(buf) - - } - is BlockchainConfig.Esplora -> { - buf.putInt(2) - this.config.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - enum class WordCount { - WORDS12,WORDS15,WORDS18,WORDS21,WORDS24; - - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): WordCount { - return liftFromRustBuffer(rbuf) { buf -> WordCount.read(buf) } - } - - internal fun read(buf: ByteBuffer) = - try { values()[buf.getInt() - 1] } - catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - buf.putInt(this.ordinal + 1) - } - } - - - - @Throws(BdkException::class) - - fun generateExtendedKey(network: Network, wordCount: WordCount, password: String? ): ExtendedKeyInfo { - val _retval = - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_generate_extended_key(network.lower(), wordCount.lower(), lowerOptionalString(password) ,status) - } - return ExtendedKeyInfo.lift(_retval) - } - - - @Throws(BdkException::class) - - fun restoreExtendedKey(network: Network, mnemonic: String, password: String? ): ExtendedKeyInfo { - val _retval = - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_restore_extended_key(network.lower(), mnemonic.lower(), lowerOptionalString(password) ,status) - } - return ExtendedKeyInfo.lift(_retval) - } - - - public interface WalletInterface { - - fun getNewAddress(): String - - fun getLastUnusedAddress(): String - - @Throws(BdkException::class) - fun getBalance(): ULong - - @Throws(BdkException::class) - fun sign(psbt: PartiallySignedBitcoinTransaction ) - - @Throws(BdkException::class) - fun getTransactions(): List - - fun getNetwork(): Network - - @Throws(BdkException::class) - fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) - - @Throws(BdkException::class) - fun broadcast(psbt: PartiallySignedBitcoinTransaction ): Transaction - - } - ---- 229,980 ---- - } - } - - internal fun 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 - } - } - - // 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. - // Error runtime. - @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 { - 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 rustCallWithError(errorHandler: CallStatusErrorHandler, 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 -+ /** -+ * @suppress -+ */ - object NullCallStatusErrorHandler: CallStatusErrorHandler { - 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 rustCall(callback: (RustCallStatus) -> U): U { - return rustCallWithError(NullCallStatusErrorHandler, callback); - } - - // Contains loading, initialization code, - // and the FFI Function declarations in a com.sun.jna.Library. - @Synchronized - private fun findLibraryName(componentName: String): String { - val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") - if (libOverride != null) { - return libOverride - } - return "bdkffi" - } - - private inline fun loadIndirect( - componentName: String - ): Lib { - return Native.load(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") - .also { lib: _UniFFILib -> - FfiConverterCallbackInterfaceBdkProgress.register(lib) - } - - } - } - - fun ffi_bdk_6983_Wallet_object_free(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_new(descriptor: RustBuffer.ByValue,change_descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_Wallet_get_new_address(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_last_unused_address(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_balance(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Long - - fun bdk_6983_Wallet_sign(ptr: Pointer,psbt: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_get_transactions(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_get_network(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_Wallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_Wallet_broadcast(ptr: Pointer,psbt: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long,fee_rate: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_PartiallySignedBitcoinTransaction_deserialize(psbt_base64: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_6983_PartiallySignedBitcoinTransaction_serialize(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_BdkProgress_init_callback(callback_stub: ForeignCallback, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_6983_generate_extended_key(network: RustBuffer.ByValue,word_count: RustBuffer.ByValue,password: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun bdk_6983_restore_extended_key(network: RustBuffer.ByValue,mnemonic: RustBuffer.ByValue,password: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_alloc(size: Int, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_6983_rustbuffer_free(buf: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Unit - - fun ffi_bdk_6983_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - - } - - // Public interface members begin here. - - // 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. -+ /** -+ * @suppress -+ */ - interface Disposable { - fun destroy() - companion object { - fun destroy(vararg args: Any?) { - args.filterIsInstance() - .forEach(Disposable::destroy) - } - } - } - -+ /** -+ * @suppress -+ */ - inline fun 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` 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` 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 - // -+ /** -+ * @suppress -+ */ - abstract class FFIObject( - protected val pointer: Pointer - ): Disposable, AutoCloseable { - - private val wasDestroyed = AtomicBoolean(false) - private 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 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() - } - } - } - } - internal typealias Handle = Long - internal class ConcurrentHandleMap( - private val leftMap: MutableMap = mutableMapOf(), - private val rightMap: MutableMap = mutableMapOf() - ) { - private val lock = java.util.concurrent.locks.ReentrantLock() - private val currentHandle = AtomicLong(0L) - private val stride = 1L - - fun insert(obj: T): Handle = - lock.withLock { - rightMap[obj] ?: - currentHandle.getAndAdd(stride) - .also { handle -> - leftMap[handle] = obj - rightMap[obj] = handle - } - } - - fun get(handle: Handle) = lock.withLock { - leftMap[handle] - } - - fun delete(handle: Handle) { - this.remove(handle) - } - - fun remove(handle: Handle): T? = - lock.withLock { - leftMap.remove(handle)?.let { obj -> - rightMap.remove(obj) - obj - } - } - } - -+ /** -+ * @suppress -+ */ - interface ForeignCallback : com.sun.jna.Callback { - public fun invoke(handle: Handle, method: Int, args: RustBuffer.ByValue, outBuf: RustBufferByReference): Int - } - - // Magic number for the Rust proxy to call using the same mechanism as every other method, - // to free the callback once it's dropped by Rust. - internal const val IDX_CALLBACK_FREE = 0 - - internal abstract class FfiConverterCallbackInterface( - protected val foreignCallback: ForeignCallback - ) { - val handleMap = ConcurrentHandleMap() - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - abstract fun register(lib: _UniFFILib) - - fun drop(handle: Handle): RustBuffer.ByValue { - return handleMap.remove(handle).let { RustBuffer.ByValue() } - } - - fun lift(n: Handle) = handleMap.get(n) - - fun read(buf: ByteBuffer) = lift(buf.getLong()) - - fun lower(v: CallbackInterface) = - handleMap.insert(v).also { - assert(handleMap.get(it) === v) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." } - } - - fun write(v: CallbackInterface, buf: RustBufferBuilder) = - buf.putLong(lower(v)) - } - - - - enum class Network { - BITCOIN,TESTNET,SIGNET,REGTEST; - -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): Network { - return liftFromRustBuffer(rbuf) { buf -> Network.read(buf) } - } - - internal fun read(buf: ByteBuffer) = - try { values()[buf.getInt() - 1] } - catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - buf.putInt(this.ordinal + 1) - } - } - - - - - - - - sealed class DatabaseConfig { - object Memory : DatabaseConfig() - - data class Sled( - val config: SledDbConfiguration - ) : DatabaseConfig() - - data class Sqlite( - val config: SqliteDbConfiguration - ) : DatabaseConfig() - -+ -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): DatabaseConfig { - return liftFromRustBuffer(rbuf) { buf -> DatabaseConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): DatabaseConfig { - return when(buf.getInt()) { - 1 -> DatabaseConfig.Memory - 2 -> DatabaseConfig.Sled( - SledDbConfiguration.read(buf) - ) - 3 -> DatabaseConfig.Sqlite( - SqliteDbConfiguration.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is DatabaseConfig.Memory -> { - buf.putInt(1) - - } - is DatabaseConfig.Sled -> { - buf.putInt(2) - this.config.write(buf) - - } - is DatabaseConfig.Sqlite -> { - buf.putInt(3) - this.config.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - - - sealed class Transaction { - - data class Unconfirmed( - val details: TransactionDetails - ) : Transaction() - - data class Confirmed( - val details: TransactionDetails, - val confirmation: BlockTime - ) : Transaction() - - -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): Transaction { - return liftFromRustBuffer(rbuf) { buf -> Transaction.read(buf) } - } - - internal fun read(buf: ByteBuffer): Transaction { - return when(buf.getInt()) { - 1 -> Transaction.Unconfirmed( - TransactionDetails.read(buf) - ) - 2 -> Transaction.Confirmed( - TransactionDetails.read(buf), - BlockTime.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is Transaction.Unconfirmed -> { - buf.putInt(1) - this.details.write(buf) - - } - is Transaction.Confirmed -> { - buf.putInt(2) - this.details.write(buf) - this.confirmation.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - - -+ /** -+ * Sealed class that can be of either blockchain configuration defined by the library. -+ */ - sealed class BlockchainConfig { - - data class Electrum( - val config: ElectrumConfig - ) : BlockchainConfig() - - data class Esplora( - val config: EsploraConfig - ) : BlockchainConfig() - - -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): BlockchainConfig { - return liftFromRustBuffer(rbuf) { buf -> BlockchainConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): BlockchainConfig { - return when(buf.getInt()) { - 1 -> BlockchainConfig.Electrum( - ElectrumConfig.read(buf) - ) - 2 -> BlockchainConfig.Esplora( - EsploraConfig.read(buf) - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - when(this) { - is BlockchainConfig.Electrum -> { - buf.putInt(1) - this.config.write(buf) - - } - is BlockchainConfig.Esplora -> { - buf.putInt(2) - this.config.write(buf) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - - - - } - - - - - - enum class WordCount { - WORDS12,WORDS15,WORDS18,WORDS21,WORDS24; - -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): WordCount { - return liftFromRustBuffer(rbuf) { buf -> WordCount.read(buf) } - } - - internal fun read(buf: ByteBuffer) = - try { values()[buf.getInt() - 1] } - catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - buf.putInt(this.ordinal + 1) - } - } - - - - @Throws(BdkException::class) - - fun generateExtendedKey(network: Network, wordCount: WordCount, password: String? ): ExtendedKeyInfo { - val _retval = - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_generate_extended_key(network.lower(), wordCount.lower(), lowerOptionalString(password) ,status) - } - return ExtendedKeyInfo.lift(_retval) - } - - - @Throws(BdkException::class) - - fun restoreExtendedKey(network: Network, mnemonic: String, password: String? ): ExtendedKeyInfo { - val _retval = - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_restore_extended_key(network.lower(), mnemonic.lower(), lowerOptionalString(password) ,status) - } - return ExtendedKeyInfo.lift(_retval) - } - - - public interface WalletInterface { - - fun getNewAddress(): String - - fun getLastUnusedAddress(): String - - @Throws(BdkException::class) - fun getBalance(): ULong - - @Throws(BdkException::class) - fun sign(psbt: PartiallySignedBitcoinTransaction ) - - @Throws(BdkException::class) - fun getTransactions(): List - - fun getNetwork(): Network - - @Throws(BdkException::class) - fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) - - @Throws(BdkException::class) - fun broadcast(psbt: PartiallySignedBitcoinTransaction ): Transaction - - } - -*************** -*** 1034,1418 **** ---- 1080,1491 ---- - } - }.let { - Transaction.lift(it) - } - - - - companion object { - internal fun lift(ptr: Pointer): Wallet { - return Wallet(ptr) - } - - internal fun read(buf: ByteBuffer): Wallet { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return Wallet.lift(Pointer(buf.getLong())) - } - - - } - } - - public interface PartiallySignedBitcoinTransactionInterface { - - fun serialize(): String - - } - - class PartiallySignedBitcoinTransaction( - pointer: Pointer - ) : FFIObject(pointer), PartiallySignedBitcoinTransactionInterface { - constructor(wallet: Wallet, recipient: String, amount: ULong, feeRate: Float? ) : - this( - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower(), lowerOptionalFloat(feeRate) ,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_6983_PartiallySignedBitcoinTransaction_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())) - } - - override fun serialize(): String = - callWithPointer { - rustCall() { status -> - _UniFFILib.INSTANCE.bdk_6983_PartiallySignedBitcoinTransaction_serialize(it, status) - } - }.let { - String.lift(it) - } - - - -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(ptr: Pointer): PartiallySignedBitcoinTransaction { - return PartiallySignedBitcoinTransaction(ptr) - } - - internal fun read(buf: ByteBuffer): PartiallySignedBitcoinTransaction { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return PartiallySignedBitcoinTransaction.lift(Pointer(buf.getLong())) - } - - fun deserialize(psbtBase64: String ): PartiallySignedBitcoinTransaction = - PartiallySignedBitcoinTransaction( - rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_6983_PartiallySignedBitcoinTransaction_deserialize(psbtBase64.lower() ,status) - }) - - } - } - - data class SledDbConfiguration ( - var path: String, - var treeName: String - ) { - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): SledDbConfiguration { - return liftFromRustBuffer(rbuf) { buf -> SledDbConfiguration.read(buf) } - } - - internal fun read(buf: ByteBuffer): SledDbConfiguration { - return SledDbConfiguration( - String.read(buf), - String.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.path.write(buf) - - this.treeName.write(buf) - - } - - - - } - - data class SqliteDbConfiguration ( - var path: String - ) { - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): SqliteDbConfiguration { - return liftFromRustBuffer(rbuf) { buf -> SqliteDbConfiguration.read(buf) } - } - - internal fun read(buf: ByteBuffer): SqliteDbConfiguration { - return SqliteDbConfiguration( - String.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.path.write(buf) - - } - - - - } - - data class TransactionDetails ( - var fees: ULong?, - var received: ULong, - var sent: ULong, - var txid: String - ) { - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): TransactionDetails { - return liftFromRustBuffer(rbuf) { buf -> TransactionDetails.read(buf) } - } - - internal fun read(buf: ByteBuffer): TransactionDetails { - return TransactionDetails( - readOptionalULong(buf), - ULong.read(buf), - ULong.read(buf), - String.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - writeOptionalULong(this.fees, buf) - - this.received.write(buf) - - this.sent.write(buf) - - this.txid.write(buf) - - } - - - - } - -+ /** -+ * Block height and timestamp of a block -+ */ - data class BlockTime ( - var height: UInt, - var timestamp: ULong - ) { -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): BlockTime { - return liftFromRustBuffer(rbuf) { buf -> BlockTime.read(buf) } - } - - internal fun read(buf: ByteBuffer): BlockTime { - return BlockTime( - UInt.read(buf), - ULong.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.height.write(buf) - - this.timestamp.write(buf) - - } - - - - } - -+ /** -+ * Configuration for an ElectrumBlockchain -+ * -+ * @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT). May start with `ssl://` or `tcp://` and include a port, `e.g. ssl://electrum.blockstream.info:60002` -+ * @property socks5 URL of the socks5 proxy server or a Tor service -+ * @property retry Request retry count -+ * @property timeout Request timeout in seconds -+ * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length -+ */ - data class ElectrumConfig ( - var url: String, - var socks5: String?, - var retry: UByte, - var timeout: UByte?, - var stopGap: ULong - ) { -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): ElectrumConfig { - return liftFromRustBuffer(rbuf) { buf -> ElectrumConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): ElectrumConfig { - return ElectrumConfig( - String.read(buf), - readOptionalString(buf), - UByte.read(buf), - readOptionalUByte(buf), - ULong.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.url.write(buf) - - writeOptionalString(this.socks5, buf) - - this.retry.write(buf) - - writeOptionalUByte(this.timeout, buf) - - this.stopGap.write(buf) - - } - - - - } - - data class EsploraConfig ( - var baseUrl: String, - var proxy: String?, - var timeoutRead: ULong, - var timeoutWrite: ULong, - var stopGap: ULong - ) { -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): EsploraConfig { - return liftFromRustBuffer(rbuf) { buf -> EsploraConfig.read(buf) } - } - - internal fun read(buf: ByteBuffer): EsploraConfig { - return EsploraConfig( - String.read(buf), - readOptionalString(buf), - ULong.read(buf), - ULong.read(buf), - ULong.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.baseUrl.write(buf) - - writeOptionalString(this.proxy, buf) - - this.timeoutRead.write(buf) - - this.timeoutWrite.write(buf) - - this.stopGap.write(buf) - - } - - - - } - - data class ExtendedKeyInfo ( - var mnemonic: String, - var xprv: String, - var fingerprint: String - ) { -+ /** -+ * @suppress -+ */ - companion object { - internal fun lift(rbuf: RustBuffer.ByValue): ExtendedKeyInfo { - return liftFromRustBuffer(rbuf) { buf -> ExtendedKeyInfo.read(buf) } - } - - internal fun read(buf: ByteBuffer): ExtendedKeyInfo { - return ExtendedKeyInfo( - String.read(buf), - String.read(buf), - String.read(buf) - ) - } - } - - internal fun lower(): RustBuffer.ByValue { - return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) - } - - internal fun write(buf: RustBufferBuilder) { - this.mnemonic.write(buf) - - this.xprv.write(buf) - - this.fingerprint.write(buf) - - } - - - - } - - - - sealed class BdkException(message: String): Exception(message) { - // Each variant is a nested class - // Flat enums carries a string error message, so no special implementation is necessary. - class InvalidU32Bytes(message: String) : BdkException(message) - class Generic(message: String) : BdkException(message) - class ScriptDoesntHaveAddressForm(message: String) : BdkException(message) - class NoRecipients(message: String) : BdkException(message) - class NoUtxosSelected(message: String) : BdkException(message) - class OutputBelowDustLimit(message: String) : BdkException(message) - class InsufficientFunds(message: String) : BdkException(message) - class BnBTotalTriesExceeded(message: String) : BdkException(message) - class BnBNoExactMatch(message: String) : BdkException(message) - class UnknownUtxo(message: String) : BdkException(message) - class TransactionNotFound(message: String) : BdkException(message) - class TransactionConfirmed(message: String) : BdkException(message) - class IrreplaceableTransaction(message: String) : BdkException(message) - class FeeRateTooLow(message: String) : BdkException(message) - class FeeTooLow(message: String) : BdkException(message) - class FeeRateUnavailable(message: String) : BdkException(message) - class MissingKeyOrigin(message: String) : BdkException(message) - class Key(message: String) : BdkException(message) - class ChecksumMismatch(message: String) : BdkException(message) - class SpendingPolicyRequired(message: String) : BdkException(message) - class InvalidPolicyPathException(message: String) : BdkException(message) - class Signer(message: String) : BdkException(message) - class InvalidNetwork(message: String) : BdkException(message) - class InvalidProgressValue(message: String) : BdkException(message) - class ProgressUpdateException(message: String) : BdkException(message) - class InvalidOutpoint(message: String) : BdkException(message) - class Descriptor(message: String) : BdkException(message) - class AddressValidator(message: String) : BdkException(message) - class Encode(message: String) : BdkException(message) - class Miniscript(message: String) : BdkException(message) - class Bip32(message: String) : BdkException(message) - class Secp256k1(message: String) : BdkException(message) - class Json(message: String) : BdkException(message) - class Hex(message: String) : BdkException(message) diff --git a/docs.patch b/docs.patch new file mode 100644 index 0000000..21cb4e9 --- /dev/null +++ b/docs.patch @@ -0,0 +1,804 @@ +*** bdk-0.5.1-undocumented.kt 2022-03-19 10:29:15.000000000 -0400 +--- bdk-0.5.1-documented.kt 2022-03-19 10:27:57.000000000 -0400 +*************** +*** 17,131 **** +--- 17,144 ---- + // 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 com.sun.jna.ptr.ByReference + import java.nio.ByteBuffer + import java.nio.ByteOrder + import java.util.concurrent.atomic.AtomicBoolean + import java.util.concurrent.atomic.AtomicLong + import java.util.concurrent.locks.ReentrantLock + import kotlin.concurrent.withLock + + // The Rust Buffer and 3 templated methods (alloc, free, reserve). + // 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. + ++ ++ /** ++ * @suppress ++ */ + @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_2b7a_rustbuffer_alloc(size, status).also { + if(it.data == null) { + throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") + } + } + } + + internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> + _UniFFILib.INSTANCE.ffi_bdk_2b7a_rustbuffer_free(buf, status) + } + + internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status -> + _UniFFILib.INSTANCE.ffi_bdk_2b7a_rustbuffer_reserve(buf, additional, status) + } + } + + @Suppress("TooGenericExceptionThrown") + fun asByteBuffer() = + this.data?.getByteBuffer(0, this.len.toLong())?.also { + it.order(ByteOrder.BIG_ENDIAN) + } + } + + /** + * The equivalent of the `*mut RustBuffer` type. + * Required for callbacks taking in an out pointer. + * + * Size is the sum of all values in the struct. + */ ++ /** ++ * @suppress ++ */ + class RustBufferByReference : ByReference(16) { + /** + * Set the pointed-to `RustBuffer` to the given value. + */ + fun setValue(value: RustBuffer.ByValue) { + // NOTE: The offsets are as they are in the C-like struct. + val pointer = getPointer() + pointer.setInt(0, value.capacity) + pointer.setInt(4, value.len) + pointer.setPointer(8, value.data) + } + } + + // 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. + ++ /** ++ * @suppress ++ */ + @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. + ++ /** ++ * @suppress ++ */ + 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 +*************** +*** 266,305 **** +--- 279,321 ---- + 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 ++ /** ++ * @suppress ++ */ + object NullCallStatusErrorHandler: CallStatusErrorHandler { + 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 rustCall(callback: (RustCallStatus) -> U): U { + return rustCallWithError(NullCallStatusErrorHandler, callback); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + @Synchronized + private fun findLibraryName(componentName: String): String { + val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") + if (libOverride != null) { + return libOverride + } +*************** +*** 406,455 **** +--- 422,477 ---- + uniffi_out_err: RustCallStatus + ): Unit + + fun ffi_bdk_2b7a_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, + uniffi_out_err: RustCallStatus + ): RustBuffer.ByValue + + + } + + // Public interface members begin here. + + // 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. ++ /** ++ * @suppress ++ */ + interface Disposable { + fun destroy() + companion object { + fun destroy(vararg args: Any?) { + args.filterIsInstance() + .forEach(Disposable::destroy) + } + } + } + ++ /** ++ * @suppress ++ */ + inline fun 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` 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: +*************** +*** 509,548 **** +--- 531,573 ---- + // 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` 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 + // ++ /** ++ * @suppress ++ */ + abstract class FFIObject( + protected val pointer: Pointer + ): Disposable, AutoCloseable { + + private val wasDestroyed = AtomicBoolean(false) + private 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() + } + } +*************** +*** 595,712 **** +--- 620,746 ---- + } + } + + fun get(handle: Handle) = lock.withLock { + leftMap[handle] + } + + fun delete(handle: Handle) { + this.remove(handle) + } + + fun remove(handle: Handle): T? = + lock.withLock { + leftMap.remove(handle)?.let { obj -> + rightMap.remove(obj) + obj + } + } + } + ++ /** ++ * @suppress ++ */ + interface ForeignCallback : com.sun.jna.Callback { + public fun invoke(handle: Handle, method: Int, args: RustBuffer.ByValue, outBuf: RustBufferByReference): Int + } + + // Magic number for the Rust proxy to call using the same mechanism as every other method, + // to free the callback once it's dropped by Rust. + internal const val IDX_CALLBACK_FREE = 0 + + internal abstract class FfiConverterCallbackInterface( + protected val foreignCallback: ForeignCallback + ) { + val handleMap = ConcurrentHandleMap() + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + abstract fun register(lib: _UniFFILib) + + fun drop(handle: Handle): RustBuffer.ByValue { + return handleMap.remove(handle).let { RustBuffer.ByValue() } + } + + fun lift(n: Handle) = handleMap.get(n) + + fun read(buf: ByteBuffer) = lift(buf.getLong()) + + fun lower(v: CallbackInterface) = + handleMap.insert(v).also { + assert(handleMap.get(it) === v) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." } + } + + fun write(v: CallbackInterface, buf: RustBufferBuilder) = + buf.putLong(lower(v)) + } + + + + enum class Network { + BITCOIN,TESTNET,SIGNET,REGTEST; + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): Network { + return liftFromRustBuffer(rbuf) { buf -> Network.read(buf) } + } + + internal fun read(buf: ByteBuffer) = + try { values()[buf.getInt() - 1] } + catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + buf.putInt(this.ordinal + 1) + } + } + + + + + + + + sealed class DatabaseConfig { + object Memory : DatabaseConfig() + + data class Sled( + val config: SledDbConfiguration + ) : DatabaseConfig() + + data class Sqlite( + val config: SqliteDbConfiguration + ) : DatabaseConfig() + + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): DatabaseConfig { + return liftFromRustBuffer(rbuf) { buf -> DatabaseConfig.read(buf) } + } + + internal fun read(buf: ByteBuffer): DatabaseConfig { + return when(buf.getInt()) { + 1 -> DatabaseConfig.Memory + 2 -> DatabaseConfig.Sled( + SledDbConfiguration.read(buf) + ) + 3 -> DatabaseConfig.Sqlite( + SqliteDbConfiguration.read(buf) + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } + } + } + + internal fun lower(): RustBuffer.ByValue { +*************** +*** 737,776 **** +--- 771,813 ---- + } + + + + + + + + sealed class Transaction { + + data class Unconfirmed( + val details: TransactionDetails + ) : Transaction() + + data class Confirmed( + val details: TransactionDetails, + val confirmation: BlockTime + ) : Transaction() + + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): Transaction { + return liftFromRustBuffer(rbuf) { buf -> Transaction.read(buf) } + } + + internal fun read(buf: ByteBuffer): Transaction { + return when(buf.getInt()) { + 1 -> Transaction.Unconfirmed( + TransactionDetails.read(buf) + ) + 2 -> Transaction.Confirmed( + TransactionDetails.read(buf), + BlockTime.read(buf) + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } + } + } + + internal fun lower(): RustBuffer.ByValue { +*************** +*** 786,836 **** +--- 823,879 ---- + } + is Transaction.Confirmed -> { + buf.putInt(2) + this.details.write(buf) + this.confirmation.write(buf) + + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + + + + } + + + + + + + ++ /** ++ * Sealed class that can be of either blockchain configuration defined by the library. ++ */ + sealed class BlockchainConfig { + + data class Electrum( + val config: ElectrumConfig + ) : BlockchainConfig() + + data class Esplora( + val config: EsploraConfig + ) : BlockchainConfig() + + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): BlockchainConfig { + return liftFromRustBuffer(rbuf) { buf -> BlockchainConfig.read(buf) } + } + + internal fun read(buf: ByteBuffer): BlockchainConfig { + return when(buf.getInt()) { + 1 -> BlockchainConfig.Electrum( + ElectrumConfig.read(buf) + ) + 2 -> BlockchainConfig.Esplora( + EsploraConfig.read(buf) + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) +*************** +*** 845,884 **** +--- 888,930 ---- + } + is BlockchainConfig.Esplora -> { + buf.putInt(2) + this.config.write(buf) + + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + + + + } + + + + + + enum class WordCount { + WORDS12,WORDS15,WORDS18,WORDS21,WORDS24; + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): WordCount { + return liftFromRustBuffer(rbuf) { buf -> WordCount.read(buf) } + } + + internal fun read(buf: ByteBuffer) = + try { values()[buf.getInt() - 1] } + catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + buf.putInt(this.ordinal + 1) + } + } +*************** +*** 1084,1123 **** +--- 1130,1172 ---- + + 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())) + } + + override fun serialize(): String = + callWithPointer { + rustCall() { status -> + _UniFFILib.INSTANCE.bdk_2b7a_PartiallySignedBitcoinTransaction_serialize(it, status) + } + }.let { + String.lift(it) + } + + + ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(ptr: Pointer): PartiallySignedBitcoinTransaction { + return PartiallySignedBitcoinTransaction(ptr) + } + + internal fun read(buf: ByteBuffer): PartiallySignedBitcoinTransaction { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return PartiallySignedBitcoinTransaction.lift(Pointer(buf.getLong())) + } + + fun deserialize(psbtBase64: String ): PartiallySignedBitcoinTransaction = + PartiallySignedBitcoinTransaction( + rustCallWithError(BdkException) { status -> + _UniFFILib.INSTANCE.bdk_2b7a_PartiallySignedBitcoinTransaction_deserialize(psbtBase64.lower() ,status) + }) + + } + } + +*************** +*** 1204,1282 **** +--- 1253,1349 ---- + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + writeOptionalULong(this.fees, buf) + + this.received.write(buf) + + this.sent.write(buf) + + this.txid.write(buf) + + } + + + + } + ++ /** ++ * Block height and timestamp of a block ++ */ + data class BlockTime ( + var height: UInt, + var timestamp: ULong + ) { ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): BlockTime { + return liftFromRustBuffer(rbuf) { buf -> BlockTime.read(buf) } + } + + internal fun read(buf: ByteBuffer): BlockTime { + return BlockTime( + UInt.read(buf), + ULong.read(buf) + ) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + this.height.write(buf) + + this.timestamp.write(buf) + + } + + + + } + ++ /** ++ * Configuration for an ElectrumBlockchain ++ * ++ * @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT). May start with `ssl://` or `tcp://` and include a port, `e.g. ssl://electrum.blockstream.info:60002` ++ * @property socks5 URL of the socks5 proxy server or a Tor service ++ * @property retry Request retry count ++ * @property timeout Request timeout in seconds ++ * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length ++ */ + data class ElectrumConfig ( + var url: String, + var socks5: String?, + var retry: UByte, + var timeout: UByte?, + var stopGap: ULong + ) { ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): ElectrumConfig { + return liftFromRustBuffer(rbuf) { buf -> ElectrumConfig.read(buf) } + } + + internal fun read(buf: ByteBuffer): ElectrumConfig { + return ElectrumConfig( + String.read(buf), + readOptionalString(buf), + UByte.read(buf), + readOptionalUByte(buf), + ULong.read(buf) + ) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + +*************** +*** 1287,1326 **** +--- 1354,1396 ---- + + this.retry.write(buf) + + writeOptionalUByte(this.timeout, buf) + + this.stopGap.write(buf) + + } + + + + } + + data class EsploraConfig ( + var baseUrl: String, + var proxy: String?, + var timeoutRead: ULong, + var timeoutWrite: ULong, + var stopGap: ULong + ) { ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): EsploraConfig { + return liftFromRustBuffer(rbuf) { buf -> EsploraConfig.read(buf) } + } + + internal fun read(buf: ByteBuffer): EsploraConfig { + return EsploraConfig( + String.read(buf), + readOptionalString(buf), + ULong.read(buf), + ULong.read(buf), + ULong.read(buf) + ) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + +*************** +*** 1329,1368 **** +--- 1399,1441 ---- + + writeOptionalString(this.proxy, buf) + + this.timeoutRead.write(buf) + + this.timeoutWrite.write(buf) + + this.stopGap.write(buf) + + } + + + + } + + data class ExtendedKeyInfo ( + var mnemonic: String, + var xprv: String, + var fingerprint: String + ) { ++ /** ++ * @suppress ++ */ + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): ExtendedKeyInfo { + return liftFromRustBuffer(rbuf) { buf -> ExtendedKeyInfo.read(buf) } + } + + internal fun read(buf: ByteBuffer): ExtendedKeyInfo { + return ExtendedKeyInfo( + String.read(buf), + String.read(buf), + String.read(buf) + ) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + this.mnemonic.write(buf) diff --git a/jvm/Module.md b/jvm/Module.md index 64d7e82..f26d669 100644 --- a/jvm/Module.md +++ b/jvm/Module.md @@ -1,4 +1,4 @@ # Module bdk-jvm -The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for the JVM. Current version: `0.4.0`. +The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for the JVM. Current version: `0.5.1`. # Package org.bitcoindevkit