Skip to content

Commit

Permalink
feat: add blockchain traits module with esplora_async impl
Browse files Browse the repository at this point in the history
  • Loading branch information
notmandatory committed Sep 30, 2023
1 parent 37d5e53 commit 8856611
Show file tree
Hide file tree
Showing 7 changed files with 479 additions and 1 deletion.
8 changes: 8 additions & 0 deletions crates/bdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ bdk_chain = { path = "../chain", version = "0.5.0", features = ["miniscript", "s
hwi = { version = "0.7.0", optional = true, features = [ "miniscript"] }
bip39 = { version = "1.0.1", optional = true }

async-trait = { version = "0.1", optional = true, features = [] }
bdk_esplora = { path = "../esplora", optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = "0.2"
js-sys = "0.3"
Expand All @@ -37,6 +40,11 @@ all-keys = ["keys-bip39"]
keys-bip39 = ["bip39"]
hardware-signer = ["hwi"]
test-hardware-signer = ["hardware-signer"]
blockchain = []
async = ["blockchain", "async-trait"]
blocking = ["blockchain"]
esplora_async = ["async", "bdk_esplora", "bdk_esplora/async"]
esplora_blocking = ["blocking", "bdk_esplora", "bdk_esplora/blocking"]

# This feature is used to run `cargo check` in our CI targeting wasm. It's not recommended
# for libraries to explicitly include the "getrandom/js" feature, so we only do it when
Expand Down
115 changes: 115 additions & 0 deletions crates/bdk/src/blockchain/async_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! Async Blockchain
//!
//! This module provides async traits that can be used to fetch [`Update`] data from the bitcoin
//! blockchain and perform other common functions needed by a [`Wallet`] user.
//!
//! Also provides async implementations of these traits for the commonly used blockchain client protocol
//! [Esplora]. Creators of new or custom blockchain clients should implement which ever of these
//! traits that are applicable.
//!
//! [Esplora]: TBD
use async_trait::async_trait;
use std::boxed::Box;

use crate::wallet::Update;
use crate::FeeRate;
use crate::Wallet;
use bitcoin::Transaction;

/// Trait that defines broadcasting a transaction by a blockchain client
#[async_trait]
pub trait Broadcast {
type Error: core::fmt::Debug;

/// Broadcast a transaction
async fn broadcast(&self, tx: &Transaction) -> Result<(), Self::Error>;
}

/// Trait that defines a function for estimating the fee rate by a blockchain backend or service.
#[async_trait]
pub trait EstimateFee {
type Error: core::fmt::Debug;
type Target: core::fmt::Debug;

/// Estimate the fee rate required to confirm a transaction in a given `target` number of blocks.
/// The `target` parameter can be customized with implementation specific features.
async fn estimate_fee(&self, target: Self::Target) -> Result<FeeRate, Self::Error>;
}

/// Trait that defines a function to scan all script pub keys (spks).
#[async_trait]
pub trait ScanSpks {
type Error: core::fmt::Debug;

/// Iterate through script pub keys (spks) of all [`Wallet`] keychains and scan each spk for
/// transactions. Scanning starts with the lowest derivation index spk and stop scanning after
/// `stop_gap` number of consecutive spks have no transaction history. A Scan is usually done
/// restoring a previously used wallet. It is a special case. Applications should use "sync"
/// style updates after an initial scan or when creating a wallet from new and never used
/// keychains. An ['Update'] is returned with newly found or updated transactions.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
async fn scan_spks(&self, wallet: &Wallet, stop_gap: usize) -> Result<Update, Self::Error>;
}

/// Trait that defines a function to sync the status of script pub keys (spks), UTXOs, and
/// unconfirmed transactions. The data to be synced can be adjusted, in an implementation specific
/// way, by providing a sync mode.
#[async_trait]
pub trait ModalSyncSpks {
type Error: core::fmt::Debug;
type SyncMode: core::fmt::Debug;

/// Iterate through derived script pub keys (spks) of [`Wallet`] keychains and get their current
/// transaction history. Also iterate through transaction UTXOs and unconfirmed transactions
/// know by the [`Wallet`] to get their current status. An [`Update`] is returned with new
/// or updated transactions and utxos. The data to be synced can be adjusted, in an
/// implementation specific way, by providing a sync mode.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
async fn sync_spks(
&self,
wallet: &Wallet,
sync_mode: Self::SyncMode,
) -> Result<Update, Self::Error>;
}

/// Trait that defines a function to sync the status of script pub keys (spks), UTXOs, and
/// unconfirmed transactions.
#[async_trait]
pub trait SyncSpks {
type Error: core::fmt::Debug;

/// Iterate through derived script pub keys (spks) of [`Wallet`] keychains and get their current
/// transaction history. Also iterate through transaction UTXOs and unconfirmed transactions
/// know by the [`Wallet`] to get their current status. An [`Update`] is returned with new
/// or updated transactions and UTXOs.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
async fn sync_spks(&self, wallet: &Wallet) -> Result<Update, Self::Error>;
}
117 changes: 117 additions & 0 deletions crates/bdk/src/blockchain/blocking_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! Blocking Blockchain
//!
//! This module provides blocking traits that can be used to fetch [`Update`] data from the bitcoin
//! blockchain and perform other common functions needed by a [`Wallet`] user.
//!
//! Also provides blocking implementations of these traits for the commonly used blockchain client protocols
//! [Electrum], [Esplora] and [BitcoinCoreRPC]. Creators of new or custom blockchain clients should
//! implement which ever of these traits that are applicable.
//!
//! [`Update`]: crate::wallet::Update
//! [Electrum]: TBD
//! [Esplora]: TBD
//! [BitcoinCoreRPC]: TBD
use crate::wallet::Update;
use crate::FeeRate;
use crate::Wallet;
use bitcoin::Transaction;

/// Trait that defines broadcasting a transaction by a blockchain client
pub trait Broadcast {
type Error: core::fmt::Debug;

/// Broadcast a transaction
fn broadcast(&self, tx: &Transaction) -> Result<(), Self::Error>;
}

// /// Which mode to use when calculating the estimated transaction fee. A conservative estimate
// /// satisfies a longer history but potentially returns a higher fee rate and is more likely
// /// to be sufficient for the desired target, but is not as responsive to short term drops in the
// /// prevailing fee market.
// enum EstimateMode {
// /// Provides a lower but possibly less accurate fee rate
// ECONOMICAL,
// /// Provides a higher fee rate that is less responsive to short term drops
// CONSERVATIVE,
// }

/// Trait that defines a function for estimating the fee rate by a blockchain backend or service.
pub trait EstimateFee {
type Error: core::fmt::Debug;
type Target: core::fmt::Debug;

/// Estimate the fee rate required to confirm a transaction in a given `target` number of blocks.
/// The `target` parameter can be customized with implementation specific features.
fn estimate_fee(&self, target: Self::Target) -> Result<FeeRate, Self::Error>;
}

/// Trait that defines a function to scan all script pub keys (spks).
pub trait ScanSpks {
type Error: core::fmt::Debug;

/// Iterate through script pub keys (spks) of all [`Wallet`] keychains and scan each spk for
/// transactions. Scanning starts with the lowest derivation index spk and stop scanning after
/// `stop_gap` number of consecutive spks have no transaction history. A Scan is usually done
/// restoring a previously used wallet. It is a special case. Applications should use "sync"
/// style updates after an initial scan or when creating a wallet from new and never used
/// keychains. An ['Update'] is returned with newly found or updated transactions.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
fn scan_spks(&self, wallet: &Wallet, stop_gap: usize) -> Result<Update, Self::Error>;
}

/// Trait that defines a function to sync the status of script pub keys (spks), UTXOs, and
/// unconfirmed transactions. The data to be synced can be adjusted, in an implementation specific
/// way, by providing a sync mode.
pub trait ModalSyncSpks {
type Error: core::fmt::Debug;
type SyncMode: core::fmt::Debug;

/// Iterate through derived script pub keys (spks) of [`Wallet`] keychains and get their current
/// transaction history. Also iterate through transaction UTXOs and unconfirmed transactions
/// know by the [`Wallet`] to get their current status. An [`Update`] is returned with new
/// or updated transactions and utxos. The data to be synced can be adjusted, in an
/// implementation specific way, by providing a sync mode.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
fn sync_spks(&self, wallet: &Wallet, sync_mode: Self::SyncMode) -> Result<Update, Self::Error>;
}

/// Trait that defines a function to sync the status of script pub keys (spks), UTXOs, and
/// unconfirmed transactions.
pub trait SyncSpks {
type Error: core::fmt::Debug;

/// Iterate through derived script pub keys (spks) of [`Wallet`] keychains and get their current
/// transaction history. Also iterate through transaction UTXOs and unconfirmed transactions
/// know by the [`Wallet`] to get their current status. An [`Update`] is returned with new
/// or updated transactions and UTXOs.
///
/// The returned update must be applied to the wallet and persisted (if a data store is being use).
/// For example:
/// ```no_run
/// # use bdk::{Wallet, wallet::Update};
/// # let mut wallet: Wallet = todo!();
/// # let update: Update = todo!();
/// wallet.apply_update(update).expect("update applied");
/// wallet.commit().expect("updates commited to db");
/// ```
fn sync_spks(&self, wallet: &Wallet) -> Result<Update, Self::Error>;
}
Loading

0 comments on commit 8856611

Please sign in to comment.