Merge pull request #13 from notmandatory/allow-creating-a-wallet-transaction
Allow creating partially signed bitcoin transactions
This commit is contained in:
commit
33a291f760
@ -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<String>) {
|
||||
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()
|
||||
}
|
||||
|
@ -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<BdkProg
|
||||
) {
|
||||
override fun register(lib: _UniFFILib) {
|
||||
rustCall() { status ->
|
||||
lib.ffi_bdk_7046_BdkProgress_init_callback(this.foreignCallback, status)
|
||||
lib.ffi_bdk_b7c7_BdkProgress_init_callback(this.foreignCallback, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,3 +105,8 @@ interface OnlineWallet {
|
||||
[Throws=BdkError]
|
||||
u64 get_balance();
|
||||
};
|
||||
|
||||
interface PartiallySignedBitcoinTransaction {
|
||||
[Throws=BdkError]
|
||||
constructor([ByRef] OnlineWallet wallet, string recipient, u64 amount);
|
||||
};
|
||||
|
31
src/lib.rs
31
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<PartiallySignedTransaction>,
|
||||
}
|
||||
|
||||
impl PartiallySignedBitcoinTransaction {
|
||||
fn new(online_wallet: &OnlineWallet, recipient: String, amount: u64) -> Result<Self, Error> {
|
||||
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user