Skip to content

Commit

Permalink
feat: add broadcast method on esplora blocking client
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderbiscuit committed Nov 17, 2023
1 parent 26352ed commit 15c1f19
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ bdk.kt

# Swift related
/.build
/.swiftpm
.swiftpm
/Packages
/*.xcodeproj
xcuserdata/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.bitcoindevkit
import org.junit.Test
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
class LiveWalletTest {
Expand All @@ -20,4 +21,38 @@ class LiveWalletTest {

assert(wallet.getBalance().total() > 0uL)
}

@Test
fun testBroadcastTransaction() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)

wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
println("New address: ${wallet.getAddress(AddressIndex.New).address}")

assert(wallet.getBalance().total() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
}

val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)

val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(4.0f)
.finish(wallet)

println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")

val walletDidSign = wallet.sign(psbt)
assertTrue(walletDidSign)

val tx: Transaction = psbt.extractTx()

println("Txid is: ${tx.txid()}")
esploraClient.broadcast(tx)
}
}
3 changes: 3 additions & 0 deletions bdk-ffi/src/bdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ interface EsploraClient {

[Throws=BdkError]
Update scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);

[Throws=BdkError]
void broadcast(Transaction transaction);
};

// ------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions bdk-ffi/src/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ impl From<BdkTransaction> for Transaction {
}
}

impl From<Transaction> for BdkTransaction {
fn from(tx: Transaction) -> Self {
tx.inner
}
}

pub struct PartiallySignedTransaction {
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
}
Expand Down
8 changes: 7 additions & 1 deletion bdk-ffi/src/esplora.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::wallet::{Update, Wallet};
use std::ops::Deref;

use bdk::wallet::Update as BdkUpdate;
use bdk::Error as BdkError;
Expand Down Expand Up @@ -56,7 +57,12 @@ impl EsploraClient {

// pub fn sync();

// pub fn broadcast();
pub fn broadcast(&self, transaction: Arc<crate::bitcoin::Transaction>) -> Result<(), BdkError> {
let bdk_transaction: bdk::bitcoin::Transaction = transaction.deref().clone().into();
self.0
.broadcast(&bdk_transaction)
.map_err(|e| BdkError::Generic(e.to_string()))
}

// pub fn estimate_fee();
}
9 changes: 4 additions & 5 deletions bdk-ffi/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::collections::HashSet;
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::OutPoint as BdkOutPoint;
use bdk::wallet::Update as BdkUpdate;
use bdk::{SignOptions, Wallet as BdkWallet};
use bdk::{Error as BdkError, FeeRate};
use bdk::{SignOptions, Wallet as BdkWallet};

use bdk::wallet::tx_builder::ChangeSpendPolicy;
use std::sync::{Arc, Mutex, MutexGuard};
Expand Down Expand Up @@ -88,10 +88,9 @@ impl Wallet {
// sign_options: Option<SignOptions>,
) -> Result<bool, BdkError> {
let mut psbt = psbt.inner.lock().unwrap();
self.get_wallet().sign(
&mut psbt,
SignOptions::default(),
).map_err(|e| BdkError::Generic(e.to_string()))
self.get_wallet()
.sign(&mut psbt, SignOptions::default())
.map_err(|e| BdkError::Generic(e.to_string()))
}
}

Expand Down
35 changes: 35 additions & 0 deletions bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/LiveWalletTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.bitcoindevkit

import kotlin.test.Test
import kotlin.test.assertTrue

class LiveWalletTest {
@Test
Expand All @@ -15,4 +16,38 @@ class LiveWalletTest {

assert(wallet.getBalance().total() > 0uL)
}

@Test
fun testBroadcastTransaction() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)

wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
println("New address: ${wallet.getAddress(AddressIndex.New).address.asString()}")

assert(wallet.getBalance().total() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
}

val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)

val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(2.0f)
.finish(wallet)

println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")

val walletDidSign = wallet.sign(psbt)
assertTrue(walletDidSign)

val tx: Transaction = psbt.extractTx()

println("Txid is: ${tx.txid()}")
esploraClient.broadcast(tx)
}
}
35 changes: 35 additions & 0 deletions bdk-python/tests/test_live_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,41 @@ def test_synced_balance(self):

self.assertGreater(wallet.get_balance().total(), 0)

def test_broadcast_transaction(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
bdk.Network.TESTNET
)
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
update = esploraClient.scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
)
wallet.apply_update(update)

self.assertGreater(wallet.get_balance().total(), 0)

recipient = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
)

psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(2.0).finish(wallet)
# print(psbt.serialize())
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")

walletDidSign = wallet.sign(psbt)
self.assertTrue(walletDidSign)
tx = psbt.extract_tx()

esploraClient.broadcast(tx)


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import XCTest
final class LiveTxBuilderTests: XCTestCase {
func testTxBuilder() throws {
let descriptor = try Descriptor(
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
Expand Down
42 changes: 41 additions & 1 deletion bdk-swift/Tests/BitcoinDevKitTests/LiveWalletTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import XCTest
final class LiveWalletTests: XCTestCase {
func testSyncedBalance() throws {
let descriptor = try Descriptor(
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
Expand All @@ -22,4 +22,44 @@ final class LiveWalletTests: XCTestCase {

XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0))
}

func testBroadcastTransaction() throws {
let descriptor = try Descriptor(
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
let update = try esploraClient.scan(
wallet: wallet,
stopGap: 10,
parallelRequests: 1
)
try wallet.applyUpdate(update: update)

XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0), "Wallet must have positive balance, please add funds")

print("Balance: \(wallet.getBalance().total())")

let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
let psbt: PartiallySignedTransaction = try
TxBuilder()
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
.feeRate(satPerVbyte: 2.0)
.finish(wallet: wallet)

print(psbt.serialize())
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")

let walletDidSign: Bool = try wallet.sign(psbt: psbt)
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")

let tx: Transaction = psbt.extractTx()
print(tx.txid())
try esploraClient.broadcast(transaction: tx)
}
}

0 comments on commit 15c1f19

Please sign in to comment.