Update EsploraConfig, Blockchain broadcast, Wallet sync

This commit is contained in:
Steve Myers 2022-04-20 23:15:14 -07:00
parent 907540d214
commit 4665c551dd
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
2 changed files with 92 additions and 52 deletions

View File

@ -101,9 +101,9 @@ dictionary ElectrumConfig {
dictionary EsploraConfig { dictionary EsploraConfig {
string base_url; string base_url;
string? proxy; string? proxy;
u64 timeout_read; u8? concurrency;
u64 timeout_write;
u64 stop_gap; u64 stop_gap;
u64? timeout;
}; };
[Enum] [Enum]
@ -112,13 +112,20 @@ interface BlockchainConfig {
Esplora(EsploraConfig config); Esplora(EsploraConfig config);
}; };
callback interface BdkProgress { interface Blockchain {
[Throws=BdkError]
constructor(BlockchainConfig config);
[Throws=BdkError]
string broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
};
callback interface Progress {
void update(f32 progress, string? message); void update(f32 progress, string? message);
}; };
interface Wallet { interface Wallet {
[Throws=BdkError] [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_new_address();
string get_last_unused_address(); string get_last_unused_address();
[Throws=BdkError] [Throws=BdkError]
@ -129,9 +136,7 @@ interface Wallet {
sequence<Transaction> get_transactions(); sequence<Transaction> get_transactions();
Network get_network(); Network get_network();
[Throws=BdkError] [Throws=BdkError]
void sync(BdkProgress progress_update, u32? max_address_param); void sync([ByRef] Blockchain blockchain, Progress? progress);
[Throws=BdkError]
string broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
}; };
interface PartiallySignedBitcoinTransaction { interface PartiallySignedBitcoinTransaction {

View File

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