Skip to content

Commit

Permalink
feat(wallet): add sync_request and full_scan_request functions
Browse files Browse the repository at this point in the history
feat(wallet): add sync_request and full_scan_request functions
  • Loading branch information
notmandatory committed Jan 18, 2024
1 parent d74d9f5 commit 28e75fc
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 5 deletions.
8 changes: 3 additions & 5 deletions crates/bdk/src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ impl fmt::Display for MiniscriptPsbtError {
impl std::error::Error for MiniscriptPsbtError {}

#[derive(Debug)]
/// Error returned from [`TxBuilder::finish`]
/// Error returned by [`TxBuilder::finish`]
///
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
/// [`TxBuilder::finish`]: super::tx_builder::TxBuilder::finish
pub enum CreateTxError<P> {
/// There was a problem with the descriptors passed in
Descriptor(DescriptorError),
Expand Down Expand Up @@ -246,9 +246,7 @@ impl<P> From<coin_selection::Error> for CreateTxError<P> {
impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for CreateTxError<P> {}

#[derive(Debug)]
/// Error returned from [`Wallet::build_fee_bump`]
///
/// [`Wallet::build_fee_bump`]: super::Wallet::build_fee_bump
/// Error returned by [`Wallet::build_fee_bump`]
pub enum BuildFeeBumpError {
/// Happens when trying to spend an UTXO that is not in the internal database
UnknownUtxo(OutPoint),
Expand Down
97 changes: 97 additions & 0 deletions crates/bdk/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use core::ops::Deref;
use descriptor::error::Error as DescriptorError;
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};

use bdk_chain::request::{FullScanRequest, SyncRequest};
use bdk_chain::tx_graph::CalculateFeeError;

pub mod coin_selection;
Expand Down Expand Up @@ -109,6 +110,38 @@ pub struct Update {
pub chain: Option<local_chain::Update>,
}

impl
From<(
BTreeMap<KeychainKind, u32>,
TxGraph<ConfirmationTimeHeightAnchor>,
local_chain::Update,
)> for Update
{
fn from(
(last_active_indices, graph, chain): (
BTreeMap<KeychainKind, u32>,
TxGraph<ConfirmationTimeHeightAnchor>,
local_chain::Update,
),
) -> Self {
Self {
last_active_indices,
graph,
chain: Some(chain),
}
}
}

impl From<(TxGraph<ConfirmationTimeHeightAnchor>, local_chain::Update)> for Update {
fn from((graph, chain): (TxGraph<ConfirmationTimeHeightAnchor>, local_chain::Update)) -> Self {
Self {
graph,
chain: Some(chain),
..Default::default()
}
}
}

/// The changes made to a wallet by applying an [`Update`].
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default)]
pub struct ChangeSet {
Expand Down Expand Up @@ -2353,6 +2386,70 @@ impl<D> Wallet<D> {
pub fn local_chain(&self) -> &LocalChain {
&self.chain
}

/// Create a [`SyncRequest`] for this wallet.
///
/// This is the first step when performing a spk-based wallet sync, the returned [`SyncRequest`] collects
/// the wallet keychain all or unused script pub keys, unconfirmed transaction id, UTXOs and local
/// chain checkpoint data needed to start a blockchain sync with a blockchain client. For a faster
/// sync set `unused_spks_only` to true to only get updates for unused wallet script pub keys (addresses).
pub fn sync_request(&self, unused_spks_only: bool) -> SyncRequest {
let checkpoint = self.latest_checkpoint();

// Sync only unused SPKs
let spks = if unused_spks_only {
self.spk_index()
.unused_spks()
.map(|(_keychain, _index, script)| ScriptBuf::from(script))
.collect::<Vec<ScriptBuf>>()
}
// Sync all SPKs
else {
self.spk_index()
.revealed_spks()
.map(|(_keychain, _index, script)| ScriptBuf::from(script))
.collect::<Vec<ScriptBuf>>()
};

// Sync UTXOs
// We want to search for whether our UTXOs are spent, and spent by which transaction.
let outpoints: Vec<OutPoint> = self.list_unspent().map(|utxo| utxo.outpoint).collect();

// Sync unconfirmed TX
// We want to search for whether our unconfirmed transactions are now confirmed.
let txids: Vec<Txid> = self
.transactions()
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
.map(|canonical_tx| canonical_tx.tx_node.txid)
.collect();

SyncRequest {
spks,
txids,
outpoints,
checkpoint,
}
}

/// Create a [`FullScanRequest] for this wallet.
///
/// This is the first step when performing a spk-based wallet full scan, the returned [`FullScanRequest]
/// collects iterators for the wallet's keychain script pub keys, and local chain checkpoint
/// data needed to start a blockchain full scan with a blockchain client.
///
/// This operation is generally only used when importing or restoring a previously used wallet
/// in which the list of used scripts is not known.
pub fn full_scan_request(
&self,
) -> FullScanRequest<KeychainKind, impl Iterator<Item = (u32, ScriptBuf)> + Clone> {
let spks_by_keychain = self.all_unbounded_spk_iters();
let checkpoint = self.latest_checkpoint();

FullScanRequest {
spks_by_keychain,
checkpoint,
}
}
}

impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet<D> {
Expand Down
1 change: 1 addition & 0 deletions example-crates/wallet_esplora_async/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bdk_wallet_esplora_async_example.dat
1 change: 1 addition & 0 deletions example-crates/wallet_esplora_blocking/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bdk_wallet_esplora_async_example.dat

0 comments on commit 28e75fc

Please sign in to comment.