Skip to content

Commit

Permalink
refactor: improve docs, cleanup unnecessary types and improve code
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirfomene committed Sep 8, 2023
1 parent 4104206 commit 1530568
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 169 deletions.
79 changes: 29 additions & 50 deletions crates/bdk/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use bdk_chain::{
keychain::{self, KeychainTxOutIndex},
local_chain::{self, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
tx_graph::{CanonicalTx, TxGraph},
Anchor, Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
IndexedTxGraph, Persist, PersistBackend,
};
use bitcoin::consensus::encode::serialize;
Expand Down Expand Up @@ -95,73 +95,51 @@ pub struct Wallet<D = ()> {
secp: SecpCtx,
}

/// A structure to update [`Wallet`].
/// An update to [`Wallet`].
///
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
#[derive(Debug, Clone)]
pub struct WalletUpdate<K, A> {
#[derive(Debug, Clone, Default)]
pub struct Update {
/// Contains the last active derivation indices per keychain (`K`), which is used to update the
/// [`KeychainTxOutIndex`].
pub last_active_indices: BTreeMap<K, u32>,
pub last_active_indices: BTreeMap<KeychainKind, u32>,

/// Update for the [`TxGraph`].
pub graph: TxGraph<A>,
/// Update for the wallet's internal [`TxGraph`].
pub graph: TxGraph<ConfirmationTimeAnchor>,

/// Update for the [`LocalChain`].
/// Update for the wallet's internal [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
pub chain: Option<local_chain::Update>,
}

impl<K, A> Default for WalletUpdate<K, A> {
fn default() -> Self {
Self {
last_active_indices: BTreeMap::new(),
graph: TxGraph::default(),
chain: None,
}
}
}

/// A structure that records the corresponding changes as result of applying an [`WalletUpdate`].
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct WalletChangeSet<K, A> {
/// The changes made to a wallet by applying an [`Update`].
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default)]
pub struct ChangeSet {
/// Changes to the [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
pub chain: local_chain::ChangeSet,

/// ChangeSet to [`IndexedTxGraph`].
/// Changes to [`IndexedTxGraph`].
///
/// [`IndexedTxGraph`]: bdk_chain::indexed_tx_graph::IndexedTxGraph
#[serde(bound(
deserialize = "K: Ord + serde::Deserialize<'de>, A: Ord + serde::Deserialize<'de>",
serialize = "K: Ord + serde::Serialize, A: Ord + serde::Serialize",
))]
pub index_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>,
pub indexed_tx_graph:
indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>,
}

impl<K, A> Default for WalletChangeSet<K, A> {
fn default() -> Self {
Self {
chain: Default::default(),
index_tx_graph: Default::default(),
}
}
}

impl<K: Ord, A: Anchor> Append for WalletChangeSet<K, A> {
impl Append for ChangeSet {
fn append(&mut self, other: Self) {
Append::append(&mut self.chain, other.chain);
Append::append(&mut self.index_tx_graph, other.index_tx_graph);
Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
}

fn is_empty(&self) -> bool {
self.chain.is_empty() && self.index_tx_graph.is_empty()
self.chain.is_empty() && self.indexed_tx_graph.is_empty()
}
}

impl<K, A> From<local_chain::ChangeSet> for WalletChangeSet<K, A> {
impl From<local_chain::ChangeSet> for ChangeSet {
fn from(chain: local_chain::ChangeSet) -> Self {
Self {
chain,
Expand All @@ -170,21 +148,22 @@ impl<K, A> From<local_chain::ChangeSet> for WalletChangeSet<K, A> {
}
}

impl<K, A> From<indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>> for WalletChangeSet<K, A> {
fn from(index_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>) -> Self {
impl From<indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>>
for ChangeSet
{
fn from(
indexed_tx_graph: indexed_tx_graph::ChangeSet<
ConfirmationTimeAnchor,
keychain::ChangeSet<KeychainKind>,
>,
) -> Self {
Self {
index_tx_graph,
indexed_tx_graph,
..Default::default()
}
}
}

/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
pub type Update = WalletUpdate<KeychainKind, ConfirmationTimeAnchor>;

/// The changeset produced internally by [`Wallet`] when mutated.
pub type ChangeSet = WalletChangeSet<KeychainKind, ConfirmationTimeAnchor>;

/// The address index selection strategy to use to derived an address from the wallet's external
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
#[derive(Debug)]
Expand Down Expand Up @@ -332,7 +311,7 @@ impl<D> Wallet<D> {

let changeset = db.load_from_persistence().map_err(NewError::Persist)?;
chain.apply_changeset(&changeset.chain);
indexed_graph.apply_changeset(changeset.index_tx_graph);
indexed_graph.apply_changeset(changeset.indexed_tx_graph);

let persist = Persist::new(db);

Expand Down
21 changes: 8 additions & 13 deletions crates/chain/src/indexed_tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloc::vec::Vec;
use bitcoin::{OutPoint, Transaction, TxOut};

use crate::{
keychain,
keychain, local_chain,
tx_graph::{self, TxGraph},
Anchor, Append,
};
Expand Down Expand Up @@ -225,7 +225,13 @@ impl<A, K> From<keychain::ChangeSet<K>> for ChangeSet<A, keychain::ChangeSet<K>>
}
}

/// Represents a structure that can index transaction data.
impl<A, IA> From<ChangeSet<A, IA>> for (local_chain::ChangeSet, ChangeSet<A, IA>) {
fn from(indexed_changeset: ChangeSet<A, IA>) -> Self {
(local_chain::ChangeSet::default(), indexed_changeset)
}
}

/// Defines methods to index transaction data.
pub trait Indexer {
/// The resultant "changeset" when new transaction data is indexed.
type ChangeSet;
Expand All @@ -234,17 +240,6 @@ pub trait Indexer {
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet;

/// Scans a transaction for relevant outpoints, which are stored and indexed internally.
///
/// If the matched script pubkey is part of the lookahead, the last stored index is updated for
/// the script pubkey's keychain and the [`ChangeSet`] returned will reflect the
/// change.
///
/// Typically, this method is used in two situations:
///
/// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
/// your txouts.
/// 2. When getting new data from the chain, you usually scan it before incorporating it into
/// your chain state.
fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet;

/// Apply changeset to itself.
Expand Down
13 changes: 7 additions & 6 deletions crates/chain/src/keychain/txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,10 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
type ChangeSet = super::ChangeSet<K>;

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
let mut changeset = super::ChangeSet::<K>::default();
for (keychain, index) in self.inner.index_txout(outpoint, txout) {
changeset.append(self.reveal_to_target(&keychain, index).1);
match self.inner.scan_txout(outpoint, txout).cloned() {
Some((keychain, index)) => self.reveal_to_target(&keychain, index).1,
None => super::ChangeSet::default(),
}
changeset
}

fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::ChangeSet {
Expand Down Expand Up @@ -175,8 +174,10 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {

/// Set the lookahead count for `keychain`.
///
/// The lookahead is the number of scripts to cache ahead of the last stored script index. This
/// is useful during a scan via [`Indexer::index_tx`] or [`Indexer::index_txout`].
/// The lookahead is the number of scripts to cache ahead of the last revealed script index. This
/// is useful to find outputs you own when processing block data that lie beyond the last revealed
/// index. In certain situations, such as when performing an initial scan of the blockchain during
/// wallet import, it may be uncertain or unknown what the last revealed index is.
///
/// # Panics
///
Expand Down
8 changes: 0 additions & 8 deletions crates/chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,3 @@ pub mod collections {

/// How many confirmations are needed f or a coinbase output to be spent.
pub const COINBASE_MATURITY: u32 = 100;

impl<A, IA> From<indexed_tx_graph::ChangeSet<A, IA>>
for (local_chain::ChangeSet, indexed_tx_graph::ChangeSet<A, IA>)
{
fn from(indexed_changeset: indexed_tx_graph::ChangeSet<A, IA>) -> Self {
(local_chain::ChangeSet::default(), indexed_changeset)
}
}
60 changes: 38 additions & 22 deletions crates/chain/src/spk_txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,35 +53,19 @@ impl<I> Default for SpkTxOutIndex<I> {
}

impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
type ChangeSet = BTreeSet<I>;
type ChangeSet = ();

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
let spk_i = self.spk_indices.get(&txout.script_pubkey);
let mut scanned_indices = BTreeSet::new();
if let Some(spk_i) = spk_i {
self.txouts.insert(outpoint, (spk_i.clone(), txout.clone()));
self.spk_txouts.insert((spk_i.clone(), outpoint));
self.unused.remove(spk_i);
scanned_indices.insert(spk_i.clone());
}
scanned_indices
self.scan_txout(outpoint, txout);
Default::default()
}

fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet {
let mut scanned_indices = BTreeSet::new();

for (i, txout) in tx.output.iter().enumerate() {
let op = OutPoint::new(tx.txid(), i as u32);
let mut txout_indices = self.index_txout(op, txout);
scanned_indices.append(&mut txout_indices);
}

scanned_indices
self.scan(tx);
Default::default()
}

fn initial_changeset(&self) -> Self::ChangeSet {
self.spks.keys().cloned().collect()
}
fn initial_changeset(&self) -> Self::ChangeSet {}

fn apply_changeset(&mut self, _changeset: Self::ChangeSet) {
// This applies nothing.
Expand All @@ -93,6 +77,38 @@ impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
}

impl<I: Clone + Ord> SpkTxOutIndex<I> {
/// Scans a transaction for txouts.
///
/// Typically, this is used in two situations:
///
/// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
/// your txouts.
/// 2. When getting new data from the chain, you usually scan it before incorporating it into your chain state.
pub fn scan(&mut self, tx: &Transaction) -> BTreeSet<I> {
let mut scanned_indices = BTreeSet::new();

for (i, txout) in tx.output.iter().enumerate() {
let op = OutPoint::new(tx.txid(), i as u32);
if let Some(spk_i) = self.scan_txout(op, txout) {
scanned_indices.insert(spk_i.clone());
}
}

scanned_indices
}

/// Scan a single `TxOut` for a matching script pubkey and returns the index that matches the
/// script pubkey (if any).
pub fn scan_txout(&mut self, op: OutPoint, txout: &TxOut) -> Option<&I> {
let spk_i = self.spk_indices.get(&txout.script_pubkey);
if let Some(spk_i) = spk_i {
self.txouts.insert(op, (spk_i.clone(), txout.clone()));
self.spk_txouts.insert((spk_i.clone(), op));
self.unused.remove(spk_i);
}
spk_i
}

/// Get a reference to the set of indexed outpoints.
pub fn outpoints(&self) -> &BTreeSet<(I, OutPoint)> {
&self.spk_txouts
Expand Down
Loading

0 comments on commit 1530568

Please sign in to comment.