diff --git a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt index bca1c3a..84c270f 100644 --- a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt +++ b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt @@ -2,24 +2,28 @@ import uniffi.bdk.* class LogProgress: BdkProgress { override fun update(progress: Float, message: String? ) { - println(progress); - println(message); + println("progress: $progress, message: $message") } } fun main(args: Array) { + println("Configuring an in-memory wallet on electrum..") val descriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; + val amount = 10000uL; + val recipient = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; val db = DatabaseConfig.Memory("") val client = BlockchainConfig.Electrum(ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 100u)) val wallet = OnlineWallet(descriptor, Network.TESTNET, db, client) val address = wallet.getNewAddress() - println("Please send satoshis to wallet address: $address") + println("Please send $amount satoshis to address: $address") readLine() println("Syncing...") wallet.sync(LogProgress(), null) val balance = wallet.getBalance() println("New wallet balance: $balance") + println("Refunding $amount satoshis to $recipient") + val psbt = PartiallySignedBitcoinTransaction(wallet, recipient, amount) println("Press any key to exit") readLine() } diff --git a/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt b/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt index 085cb16..b7777af 100644 --- a/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt +++ b/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt @@ -44,15 +44,15 @@ open class RustBuffer : Structure() { companion object { internal fun alloc(size: Int = 0) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_7046_rustbuffer_alloc(size, status) + _UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_alloc(size, status) } internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_7046_rustbuffer_free(buf, status) + _UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_free(buf, status) } internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_7046_rustbuffer_reserve(buf, additional, status) + _UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_reserve(buf, additional, status) } } @@ -392,6 +392,12 @@ internal fun String.write(buf: RustBufferBuilder) { + + + + + + @@ -542,59 +548,67 @@ internal interface _UniFFILib : Library { } } - fun ffi_bdk_7046_OfflineWallet_object_free(ptr: Pointer, + fun ffi_bdk_b7c7_OfflineWallet_object_free(ptr: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun bdk_7046_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue, + fun bdk_b7c7_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Pointer - fun bdk_7046_OfflineWallet_get_new_address(ptr: Pointer, + fun bdk_b7c7_OfflineWallet_get_new_address(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun ffi_bdk_7046_OnlineWallet_object_free(ptr: Pointer, + fun ffi_bdk_b7c7_OnlineWallet_object_free(ptr: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun bdk_7046_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, + fun bdk_b7c7_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Pointer - fun bdk_7046_OnlineWallet_get_new_address(ptr: Pointer, + fun bdk_b7c7_OnlineWallet_get_new_address(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun bdk_7046_OnlineWallet_get_network(ptr: Pointer, + fun bdk_b7c7_OnlineWallet_get_network(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun bdk_7046_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, + fun bdk_b7c7_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Unit - fun bdk_7046_OnlineWallet_get_balance(ptr: Pointer, + fun bdk_b7c7_OnlineWallet_get_balance(ptr: Pointer, uniffi_out_err: RustCallStatus ): Long - fun ffi_bdk_7046_BdkProgress_init_callback(callback_stub: ForeignCallback, + fun ffi_bdk_b7c7_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun ffi_bdk_7046_rustbuffer_alloc(size: Int, + fun bdk_b7c7_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long, uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue + ): Pointer - fun ffi_bdk_7046_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, - uniffi_out_err: RustCallStatus - ): RustBuffer.ByValue - - fun ffi_bdk_7046_rustbuffer_free(buf: RustBuffer.ByValue, + fun ffi_bdk_b7c7_BdkProgress_init_callback(callback_stub: ForeignCallback, uniffi_out_err: RustCallStatus ): Unit - fun ffi_bdk_7046_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, + fun ffi_bdk_b7c7_rustbuffer_alloc(size: Int, + uniffi_out_err: RustCallStatus + ): RustBuffer.ByValue + + fun ffi_bdk_b7c7_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, + uniffi_out_err: RustCallStatus + ): RustBuffer.ByValue + + fun ffi_bdk_b7c7_rustbuffer_free(buf: RustBuffer.ByValue, + uniffi_out_err: RustCallStatus + ): Unit + + fun ffi_bdk_b7c7_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue @@ -1304,7 +1318,7 @@ class OfflineWallet( constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig ) : this( rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_7046_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status) + _UniFFILib.INSTANCE.bdk_b7c7_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status) }) /** @@ -1317,7 +1331,7 @@ class OfflineWallet( */ override protected fun freeRustArcPtr() { rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_7046_OfflineWallet_object_free(this.pointer, status) + _UniFFILib.INSTANCE.ffi_bdk_b7c7_OfflineWallet_object_free(this.pointer, status) } } @@ -1332,7 +1346,7 @@ class OfflineWallet( override fun getNewAddress(): String = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_7046_OfflineWallet_get_new_address(it, status) + _UniFFILib.INSTANCE.bdk_b7c7_OfflineWallet_get_new_address(it, status) } }.let { String.lift(it) @@ -1371,7 +1385,7 @@ class OnlineWallet( constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig, blockchainConfig: BlockchainConfig ) : this( rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_7046_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status) + _UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status) }) /** @@ -1384,7 +1398,7 @@ class OnlineWallet( */ override protected fun freeRustArcPtr() { rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_7046_OnlineWallet_object_free(this.pointer, status) + _UniFFILib.INSTANCE.ffi_bdk_b7c7_OnlineWallet_object_free(this.pointer, status) } } @@ -1399,7 +1413,7 @@ class OnlineWallet( override fun getNewAddress(): String = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_7046_OnlineWallet_get_new_address(it, status) + _UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_new_address(it, status) } }.let { String.lift(it) @@ -1408,7 +1422,7 @@ class OnlineWallet( override fun getNetwork(): Network = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_7046_OnlineWallet_get_network(it, status) + _UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_network(it, status) } }.let { Network.lift(it) @@ -1417,14 +1431,14 @@ class OnlineWallet( override fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_7046_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status) + _UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status) } } override fun getBalance(): ULong = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_7046_OnlineWallet_get_balance(it, status) + _UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_balance(it, status) } }.let { ULong.lift(it) @@ -1447,6 +1461,60 @@ class OnlineWallet( } } +@ExperimentalUnsignedTypes +public interface PartiallySignedBitcoinTransactionInterface { + +} + +@ExperimentalUnsignedTypes +class PartiallySignedBitcoinTransaction( + pointer: Pointer +) : FFIObject(pointer), PartiallySignedBitcoinTransactionInterface { + constructor(wallet: OnlineWallet, recipient: String, amount: ULong ) : + this( + rustCallWithError(BdkException) { status -> + _UniFFILib.INSTANCE.bdk_b7c7_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.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_b7c7_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())) + } + + + + 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())) + } + + + } +} + // Callback Interfaces @@ -1496,7 +1564,7 @@ internal object CallbackInterfaceBdkProgressInternals: CallbackInternals - lib.ffi_bdk_7046_BdkProgress_init_callback(this.foreignCallback, status) + lib.ffi_bdk_b7c7_BdkProgress_init_callback(this.foreignCallback, status) } } } diff --git a/src/bdk.udl b/src/bdk.udl index 0283237..6bc08b0 100644 --- a/src/bdk.udl +++ b/src/bdk.udl @@ -105,3 +105,8 @@ interface OnlineWallet { [Throws=BdkError] u64 get_balance(); }; + +interface PartiallySignedBitcoinTransaction { + [Throws=BdkError] + constructor([ByRef] OnlineWallet wallet, string recipient, u64 amount); +}; diff --git a/src/lib.rs b/src/lib.rs index 9bd5669..99f50ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ -use bdk::bitcoin::Network; +use bdk::address_validator::AddressValidatorError; +use bdk::bitcoin::util::psbt::PartiallySignedTransaction; +use bdk::bitcoin::{Address, Network}; use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; use bdk::blockchain::Progress; use bdk::blockchain::{ @@ -7,9 +9,9 @@ use bdk::blockchain::{ use bdk::database::any::{AnyDatabase, SledDbConfiguration}; use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase}; use bdk::wallet::AddressIndex; -use bdk::Error; -use bdk::Wallet; +use bdk::{Error, Wallet}; use std::convert::TryFrom; +use std::str::FromStr; use std::sync::{Mutex, MutexGuard}; uniffi_macros::include_scaffolding!("bdk"); @@ -103,6 +105,29 @@ impl Progress for BdkProgressHolder { } } +struct PartiallySignedBitcoinTransaction { + internal: Mutex, +} + +impl PartiallySignedBitcoinTransaction { + fn new(online_wallet: &OnlineWallet, recipient: String, amount: u64) -> Result { + let wallet = online_wallet.get_wallet(); + match Address::from_str(&recipient) { + Ok(address) => { + let mut builder = wallet.build_tx(); + builder.add_recipient(address.script_pubkey(), amount); + let (pst, ..) = builder.finish()?; + Ok(PartiallySignedBitcoinTransaction { + internal: Mutex::new(pst), + }) + } + Err(..) => Err(BdkError::AddressValidator( + AddressValidatorError::InvalidScript, + )), + } + } +} + impl OnlineWallet { fn new( descriptor: String,