Add sign and broadcast to wallet

This commit is contained in:
Sudarsan Balaji 2021-10-16 20:19:34 +05:30
parent 9d6229df58
commit 320771d7f8
4 changed files with 105 additions and 45 deletions

View File

@ -9,11 +9,11 @@ class LogProgress: BdkProgress {
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;
"pkh(cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR)";
val amount = 1000uL;
val recipient = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt";
val db = DatabaseConfig.Memory("")
val client = BlockchainConfig.Electrum(ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 100u))
val client = BlockchainConfig.Electrum(ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u))
val wallet = OnlineWallet(descriptor, Network.TESTNET, db, client)
val address = wallet.getNewAddress()
println("Please send $amount satoshis to address: $address")
@ -22,8 +22,19 @@ fun main(args: Array<String>) {
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")
println("Press Enter to return funds")
readLine()
println("Creating a partially signed bitcoin transaction with recipient $recipient and amount $amount satoshis...")
val transaction = PartiallySignedBitcoinTransaction(wallet, recipient, amount)
println("Signing the transaction...")
wallet.sign(transaction)
println("Broadcasting the signed transaction...")
val transactionId = wallet.broadcast(transaction)
println("Refunded $amount satoshis to $recipient via transaction id $transactionId")
println("Syncing...")
wallet.sync(LogProgress(), null)
val final_balance = wallet.getBalance()
println("New wallet balance: $final_balance")
println("Press Enter to exit")
readLine()
}

View File

@ -44,15 +44,15 @@ open class RustBuffer : Structure() {
companion object {
internal fun alloc(size: Int = 0) = rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_alloc(size, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_rustbuffer_alloc(size, status)
}
internal fun free(buf: RustBuffer.ByValue) = rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_free(buf, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_rustbuffer_free(buf, status)
}
internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_rustbuffer_reserve(buf, additional, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_rustbuffer_reserve(buf, additional, status)
}
}
@ -548,67 +548,75 @@ internal interface _UniFFILib : Library {
}
}
fun ffi_bdk_b7c7_OfflineWallet_object_free(ptr: Pointer,
fun ffi_bdk_b468_OfflineWallet_object_free(ptr: Pointer,
uniffi_out_err: RustCallStatus
): Unit
fun bdk_b7c7_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,
fun bdk_b468_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,
uniffi_out_err: RustCallStatus
): Pointer
fun bdk_b7c7_OfflineWallet_get_new_address(ptr: Pointer,
fun bdk_b468_OfflineWallet_get_new_address(ptr: Pointer,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun ffi_bdk_b7c7_OnlineWallet_object_free(ptr: Pointer,
fun ffi_bdk_b468_OnlineWallet_object_free(ptr: Pointer,
uniffi_out_err: RustCallStatus
): Unit
fun bdk_b7c7_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue,
fun bdk_b468_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue,
uniffi_out_err: RustCallStatus
): Pointer
fun bdk_b7c7_OnlineWallet_get_new_address(ptr: Pointer,
fun bdk_b468_OnlineWallet_get_new_address(ptr: Pointer,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun bdk_b7c7_OnlineWallet_get_network(ptr: Pointer,
fun bdk_b468_OnlineWallet_get_network(ptr: Pointer,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun bdk_b7c7_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue,
fun bdk_b468_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue,
uniffi_out_err: RustCallStatus
): Unit
fun bdk_b7c7_OnlineWallet_get_balance(ptr: Pointer,
fun bdk_b468_OnlineWallet_get_balance(ptr: Pointer,
uniffi_out_err: RustCallStatus
): Long
fun ffi_bdk_b7c7_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer,
fun bdk_b468_OnlineWallet_sign(ptr: Pointer,psbt: Pointer,
uniffi_out_err: RustCallStatus
): Unit
fun bdk_b7c7_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long,
fun bdk_b468_OnlineWallet_broadcast(ptr: Pointer,psbt: Pointer,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun ffi_bdk_b468_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer,
uniffi_out_err: RustCallStatus
): Unit
fun bdk_b468_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long,
uniffi_out_err: RustCallStatus
): Pointer
fun ffi_bdk_b7c7_BdkProgress_init_callback(callback_stub: ForeignCallback,
fun ffi_bdk_b468_BdkProgress_init_callback(callback_stub: ForeignCallback,
uniffi_out_err: RustCallStatus
): Unit
fun ffi_bdk_b7c7_rustbuffer_alloc(size: Int,
fun ffi_bdk_b468_rustbuffer_alloc(size: Int,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun ffi_bdk_b7c7_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue,
fun ffi_bdk_b468_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
fun ffi_bdk_b7c7_rustbuffer_free(buf: RustBuffer.ByValue,
fun ffi_bdk_b468_rustbuffer_free(buf: RustBuffer.ByValue,
uniffi_out_err: RustCallStatus
): Unit
fun ffi_bdk_b7c7_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int,
fun ffi_bdk_b468_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int,
uniffi_out_err: RustCallStatus
): RustBuffer.ByValue
@ -1318,7 +1326,7 @@ class OfflineWallet(
constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig ) :
this(
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status)
_UniFFILib.INSTANCE.bdk_b468_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status)
})
/**
@ -1331,7 +1339,7 @@ class OfflineWallet(
*/
override protected fun freeRustArcPtr() {
rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_OfflineWallet_object_free(this.pointer, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_OfflineWallet_object_free(this.pointer, status)
}
}
@ -1346,7 +1354,7 @@ class OfflineWallet(
override fun getNewAddress(): String =
callWithPointer {
rustCall() { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OfflineWallet_get_new_address(it, status)
_UniFFILib.INSTANCE.bdk_b468_OfflineWallet_get_new_address(it, status)
}
}.let {
String.lift(it)
@ -1375,6 +1383,8 @@ public interface OnlineWalletInterface {
fun getNetwork(): Network
fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? )
fun getBalance(): ULong
fun sign(psbt: PartiallySignedBitcoinTransaction )
fun broadcast(psbt: PartiallySignedBitcoinTransaction ): String
}
@ -1385,7 +1395,7 @@ class OnlineWallet(
constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig, blockchainConfig: BlockchainConfig ) :
this(
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status)
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status)
})
/**
@ -1398,7 +1408,7 @@ class OnlineWallet(
*/
override protected fun freeRustArcPtr() {
rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_OnlineWallet_object_free(this.pointer, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_OnlineWallet_object_free(this.pointer, status)
}
}
@ -1413,7 +1423,7 @@ class OnlineWallet(
override fun getNewAddress(): String =
callWithPointer {
rustCall() { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_new_address(it, status)
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_get_new_address(it, status)
}
}.let {
String.lift(it)
@ -1422,7 +1432,7 @@ class OnlineWallet(
override fun getNetwork(): Network =
callWithPointer {
rustCall() { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_network(it, status)
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_get_network(it, status)
}
}.let {
Network.lift(it)
@ -1431,19 +1441,35 @@ class OnlineWallet(
override fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) =
callWithPointer {
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status)
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status)
}
}
override fun getBalance(): ULong =
callWithPointer {
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b7c7_OnlineWallet_get_balance(it, status)
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_get_balance(it, status)
}
}.let {
ULong.lift(it)
}
override fun sign(psbt: PartiallySignedBitcoinTransaction ) =
callWithPointer {
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_sign(it, psbt.lower() , status)
}
}
override fun broadcast(psbt: PartiallySignedBitcoinTransaction ): String =
callWithPointer {
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b468_OnlineWallet_broadcast(it, psbt.lower() , status)
}
}.let {
String.lift(it)
}
companion object {
@ -1473,7 +1499,7 @@ class PartiallySignedBitcoinTransaction(
constructor(wallet: OnlineWallet, recipient: String, amount: ULong ) :
this(
rustCallWithError(BdkException) { status ->
_UniFFILib.INSTANCE.bdk_b7c7_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower() ,status)
_UniFFILib.INSTANCE.bdk_b468_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower() ,status)
})
/**
@ -1486,7 +1512,7 @@ class PartiallySignedBitcoinTransaction(
*/
override protected fun freeRustArcPtr() {
rustCall() { status ->
_UniFFILib.INSTANCE.ffi_bdk_b7c7_PartiallySignedBitcoinTransaction_object_free(this.pointer, status)
_UniFFILib.INSTANCE.ffi_bdk_b468_PartiallySignedBitcoinTransaction_object_free(this.pointer, status)
}
}
@ -1564,7 +1590,7 @@ internal object CallbackInterfaceBdkProgressInternals: CallbackInternals<BdkProg
) {
override fun register(lib: _UniFFILib) {
rustCall() { status ->
lib.ffi_bdk_b7c7_BdkProgress_init_callback(this.foreignCallback, status)
lib.ffi_bdk_b468_BdkProgress_init_callback(this.foreignCallback, status)
}
}
}

View File

@ -104,6 +104,10 @@ interface OnlineWallet {
void sync(BdkProgress progress_update, u32? max_address_param);
[Throws=BdkError]
u64 get_balance();
[Throws=BdkError]
void sign([ByRef] PartiallySignedBitcoinTransaction psbt);
[Throws=BdkError]
string broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
};
interface PartiallySignedBitcoinTransaction {

View File

@ -1,4 +1,3 @@
use bdk::address_validator::AddressValidatorError;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Network};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
@ -9,7 +8,7 @@ use bdk::blockchain::{
use bdk::database::any::{AnyDatabase, SledDbConfiguration};
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
use bdk::wallet::AddressIndex;
use bdk::{Error, Wallet};
use bdk::{Error, SignOptions, Wallet};
use std::convert::TryFrom;
use std::str::FromStr;
use std::sync::{Mutex, MutexGuard};
@ -114,15 +113,17 @@ impl PartiallySignedBitcoinTransaction {
let wallet = online_wallet.get_wallet();
match Address::from_str(&recipient) {
Ok(address) => {
let (psbt, _) = {
let mut builder = wallet.build_tx();
builder.add_recipient(address.script_pubkey(), amount);
let (pst, ..) = builder.finish()?;
builder.finish()?
};
Ok(PartiallySignedBitcoinTransaction {
internal: Mutex::new(pst),
internal: Mutex::new(psbt),
})
}
Err(..) => Err(BdkError::AddressValidator(
AddressValidatorError::InvalidScript,
Err(..) => Err(BdkError::Generic(
"failed to read wallet address".to_string(),
)),
}
}
@ -190,6 +191,24 @@ impl OnlineWallet {
fn get_balance(&self) -> Result<u64, Error> {
self.wallet.lock().unwrap().get_balance()
}
fn sign<'a>(&self, psbt: &'a PartiallySignedBitcoinTransaction) -> Result<(), Error> {
let mut psbt = psbt.internal.lock().unwrap();
let finalized = self.get_wallet().sign(&mut psbt, SignOptions::default())?;
match finalized {
true => Ok(()),
false => Err(BdkError::Generic(format!(
"transaction signing not finalized {:?}",
psbt
))),
}
}
fn broadcast<'a>(&self, psbt: &'a PartiallySignedBitcoinTransaction) -> Result<String, Error> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
let tx_id = self.get_wallet().broadcast(tx)?;
Ok(tx_id.to_string())
}
}
impl WalletHolder<AnyBlockchain> for OnlineWallet {