Merge bitcoindevkit/bdk-ffi#149: Update to bdk 0.18.0

f38f4c6197ec95376beeb2f444f85bd6a8a12fbd Update CHANGELOG (Steve Myers)
11ba16ec1bec922e5ffd7fcf3903eec9caf227e7 Move txid getter to PartiallySignedBitcoinTransaction (Steve Myers)
4665c551dd67f38fe91011e41ed5d3e8de404fcf Update EsploraConfig, Blockchain broadcast, Wallet sync (Steve Myers)
907540d2143b4eff4146643259a068b2f0c8696a Update bdk to 0.18.0 with sqlite-bundled feature (Steve Myers)

Pull request description:

  Changes that were needed to match updated bdk APIs:
  * new bdk-ffi `Blockchain`  interface with `constructo(BlockchainConfig)` and `broadcast()` functions
  * `Blockchain.broadcast()` function is now void (doesn't return txid)
  * added `PartiallySignedBitcoinTransaction.txid()` getter function
  * `Wallet.sync()` function now takes a `Blockchain` and optional `Progress` callback interface

  Tests performed:
  * from `bdk-kotlin/bdk-ffi` added local remote to this branch, ran build.sh script
  * fixed broken `bdk-kotlin` jvm and android tests and confirmed tests all pass

Top commit has no ACKs.

Tree-SHA512: d50633bbc8fd8a0d141597b30122c72957d2a0d64fc1537b649eeb8a5df1b1fb9a78ee1f03b9c606f47dee3952c9a91ae09eb47eb7a66d5f0fcb5545c86d906b
This commit is contained in:
Steve Myers 2022-05-05 09:29:18 -07:00
commit 3b243efefd
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
4 changed files with 97 additions and 56 deletions

View File

@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Update BDK to version 0.18.0
## [v0.5.0]
- Fix Wallet.broadcast function, now returns a tx id as a hex string

View File

@ -14,10 +14,7 @@ crate-type = ["staticlib", "cdylib"]
name = "bdkffi"
[dependencies]
bdk = { version = "0.14", features = ["all-keys", "use-esplora-ureq", "sqlite"] }
# TODO remove when bdk "sqlite-bundled" feature added
rusqlite = { version = "0.25.3", features = ["bundled"] }
bdk = { version = "0.18", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
uniffi_macros = { version = "0.16.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.16.0", features = ["builtin-bindgen"] }

View File

@ -101,9 +101,9 @@ dictionary ElectrumConfig {
dictionary EsploraConfig {
string base_url;
string? proxy;
u64 timeout_read;
u64 timeout_write;
u8? concurrency;
u64 stop_gap;
u64? timeout;
};
[Enum]
@ -112,13 +112,20 @@ interface BlockchainConfig {
Esplora(EsploraConfig config);
};
callback interface BdkProgress {
interface Blockchain {
[Throws=BdkError]
constructor(BlockchainConfig config);
[Throws=BdkError]
void broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
};
callback interface Progress {
void update(f32 progress, string? message);
};
interface Wallet {
[Throws=BdkError]
constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config, BlockchainConfig blockchain_config);
constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config);
string get_new_address();
string get_last_unused_address();
[Throws=BdkError]
@ -129,15 +136,14 @@ interface Wallet {
sequence<Transaction> get_transactions();
Network get_network();
[Throws=BdkError]
void sync(BdkProgress progress_update, u32? max_address_param);
[Throws=BdkError]
string broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
void sync([ByRef] Blockchain blockchain, Progress? progress);
};
interface PartiallySignedBitcoinTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
string serialize();
string txid();
};
interface TxBuilder {

View File

@ -3,18 +3,22 @@ use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Network, Script};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::Progress;
use bdk::blockchain::{
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig, ConfigurableBlockchain,
};
use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress};
use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
use bdk::keys::bip39::{Language, Mnemonic, WordCount};
use bdk::keys::{DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey};
use bdk::miniscript::BareCtx;
use bdk::wallet::AddressIndex;
use bdk::{BlockTime, Error, FeeRate, SignOptions, Wallet as BdkWallet};
use bdk::{
BlockTime, Error, FeeRate, SignOptions, SyncOptions as BdkSyncOptions, Wallet as BdkWallet,
};
use std::convert::TryFrom;
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard};
@ -39,9 +43,9 @@ pub struct ElectrumConfig {
pub struct EsploraConfig {
pub base_url: String,
pub proxy: Option<String>,
pub timeout_read: u64,
pub timeout_write: u64,
pub concurrency: Option<u8>,
pub stop_gap: u64,
pub timeout: Option<u64>,
}
pub enum BlockchainConfig {
@ -93,25 +97,73 @@ impl From<&bdk::TransactionDetails> for Transaction {
}
}
struct Wallet {
wallet_mutex: Mutex<BdkWallet<AnyBlockchain, AnyDatabase>>,
struct Blockchain {
blockchain_mutex: Mutex<AnyBlockchain>,
}
pub trait BdkProgress: Send + Sync {
impl Blockchain {
fn new(blockchain_config: BlockchainConfig) -> Result<Self, BdkError> {
let any_blockchain_config = match blockchain_config {
BlockchainConfig::Electrum { config } => {
AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
retry: config.retry,
socks5: config.socks5,
timeout: config.timeout,
url: config.url,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
})
}
BlockchainConfig::Esplora { config } => {
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: config.base_url,
proxy: config.proxy,
concurrency: config.concurrency,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
timeout: config.timeout,
})
}
};
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
Ok(Self {
blockchain_mutex: Mutex::new(blockchain),
})
}
fn get_blockchain(&self) -> MutexGuard<AnyBlockchain> {
self.blockchain_mutex.lock().expect("blockchain")
}
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<(), Error> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
self.get_blockchain().broadcast(&tx)
}
}
struct Wallet {
wallet_mutex: Mutex<BdkWallet<AnyDatabase>>,
}
pub trait Progress: Send + Sync + 'static {
fn update(&self, progress: f32, message: Option<String>);
}
struct BdkProgressHolder {
progress_update: Box<dyn BdkProgress>,
struct ProgressHolder {
progress: Box<dyn Progress>,
}
impl Progress for BdkProgressHolder {
impl BdkProgress for ProgressHolder {
fn update(&self, progress: f32, message: Option<String>) -> Result<(), Error> {
self.progress_update.update(progress, message);
self.progress.update(progress, message);
Ok(())
}
}
impl fmt::Debug for ProgressHolder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProgressHolder").finish_non_exhaustive()
}
}
#[derive(Debug)]
struct PartiallySignedBitcoinTransaction {
internal: Mutex<PartiallySignedTransaction>,
@ -129,6 +181,12 @@ impl PartiallySignedBitcoinTransaction {
let psbt = self.internal.lock().unwrap().clone();
psbt.to_string()
}
fn txid(&self) -> String {
let tx = self.internal.lock().unwrap().clone().extract_tx();
let txid = tx.txid();
txid.to_hex()
}
}
impl Wallet {
@ -137,46 +195,23 @@ impl Wallet {
change_descriptor: Option<String>,
network: Network,
database_config: DatabaseConfig,
blockchain_config: BlockchainConfig,
) -> Result<Self, BdkError> {
let any_database_config = match database_config {
DatabaseConfig::Memory => AnyDatabaseConfig::Memory(()),
DatabaseConfig::Sled { config } => AnyDatabaseConfig::Sled(config),
DatabaseConfig::Sqlite { config } => AnyDatabaseConfig::Sqlite(config),
};
let any_blockchain_config = match blockchain_config {
BlockchainConfig::Electrum { config } => {
AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
retry: config.retry,
socks5: config.socks5,
timeout: config.timeout,
url: config.url,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
})
}
BlockchainConfig::Esplora { config } => {
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: config.base_url,
proxy: config.proxy,
timeout_read: config.timeout_read,
timeout_write: config.timeout_write,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
})
}
};
let database = AnyDatabase::from_config(&any_database_config)?;
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
let wallet_mutex = Mutex::new(BdkWallet::new(
&descriptor,
change_descriptor.as_ref(),
network,
database,
blockchain,
)?);
Ok(Wallet { wallet_mutex })
}
fn get_wallet(&self) -> MutexGuard<BdkWallet<AnyBlockchain, AnyDatabase>> {
fn get_wallet(&self) -> MutexGuard<BdkWallet<AnyDatabase>> {
self.wallet_mutex.lock().expect("wallet")
}
@ -186,11 +221,18 @@ impl Wallet {
fn sync(
&self,
progress_update: Box<dyn BdkProgress>,
max_address_param: Option<u32>,
blockchain: &Blockchain,
progress: Option<Box<dyn Progress>>,
) -> Result<(), BdkError> {
self.get_wallet()
.sync(BdkProgressHolder { progress_update }, max_address_param)
let bdk_sync_opts = BdkSyncOptions {
progress: progress.map(|p| {
Box::new(ProgressHolder { progress: p })
as Box<(dyn bdk::blockchain::Progress + 'static)>
}),
};
let blockchain = blockchain.get_blockchain();
self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
}
fn get_new_address(&self) -> String {
@ -229,12 +271,6 @@ impl Wallet {
let transactions = self.get_wallet().list_transactions(true)?;
Ok(transactions.iter().map(Transaction::from).collect())
}
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<String, Error> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
let txid = self.get_wallet().broadcast(&tx)?;
Ok(txid.to_hex())
}
}
pub struct ExtendedKeyInfo {