Skip to content

Commit c20a4da

Browse files
committed
Merge #1084: Enhance bdk chain structures
1ff806c fix(chain)!: rm weird `From` impl (志宇) d43ae02 refactor: improve docs, cleanup unnecessary types and improve code (Vladimir Fomene) 4104206 feat: impl Append for lots of tuples (LLFourn) c56728f refactor: Remove `scan` and `scan_txout` from SpkTxoutIndex and KeychainTxoutIndex (Vladimir Fomene) 32c40ac feat(electrum)!: change signature of `ElectrumExt` (志宇) a28748c refactor: Implement Default for WalletUpdate (Vladimir Fomene) f42f8b8 refactor: Allow for no chain update (Vladimir Fomene) 68572bf refactor: move WalletChangeset to wallet module (Vladimir Fomene) 2392e50 refactor: Move WalletUpdate to wallet module (Vladimir Fomene) 7c12dc9 refactor: Remove ForEachTxout trait (Vladimir Fomene) 6bcbb93 refactor: Edit ElectrumExt not to use WalletUpdate (Vladimir Fomene) Pull request description: ### Description Fixes #1061 ### Changelog notice - Move WalletUpdate to the wallet module - Remove ForEachTxout trait completely - Refactor ElectrumExt to not use WalletUpdate. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ACKs for top commit: evanlinjin: ACK 1ff806c Tree-SHA512: 05349713af9d2efa14a522ceaabb7513bb437d786adf2f93055765589a67e4eb68bda36ff415aeba07816c4d30988d4d55bac018e7697019270a219105ed65a2
2 parents 59fc1b3 + 1ff806c commit c20a4da

File tree

17 files changed

+325
-393
lines changed

17 files changed

+325
-393
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use alloc::{
2222
pub use bdk_chain::keychain::Balance;
2323
use bdk_chain::{
2424
indexed_tx_graph,
25-
keychain::{KeychainTxOutIndex, WalletChangeSet, WalletUpdate},
25+
keychain::{self, KeychainTxOutIndex},
2626
local_chain::{self, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
2727
tx_graph::{CanonicalTx, TxGraph},
2828
Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
@@ -95,11 +95,74 @@ pub struct Wallet<D = ()> {
9595
secp: SecpCtx,
9696
}
9797

98-
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
99-
pub type Update = WalletUpdate<KeychainKind, ConfirmationTimeAnchor>;
98+
/// An update to [`Wallet`].
99+
///
100+
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
101+
#[derive(Debug, Clone, Default)]
102+
pub struct Update {
103+
/// Contains the last active derivation indices per keychain (`K`), which is used to update the
104+
/// [`KeychainTxOutIndex`].
105+
pub last_active_indices: BTreeMap<KeychainKind, u32>,
106+
107+
/// Update for the wallet's internal [`TxGraph`].
108+
pub graph: TxGraph<ConfirmationTimeAnchor>,
109+
110+
/// Update for the wallet's internal [`LocalChain`].
111+
///
112+
/// [`LocalChain`]: local_chain::LocalChain
113+
pub chain: Option<local_chain::Update>,
114+
}
115+
116+
/// The changes made to a wallet by applying an [`Update`].
117+
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default)]
118+
pub struct ChangeSet {
119+
/// Changes to the [`LocalChain`].
120+
///
121+
/// [`LocalChain`]: local_chain::LocalChain
122+
pub chain: local_chain::ChangeSet,
123+
124+
/// Changes to [`IndexedTxGraph`].
125+
///
126+
/// [`IndexedTxGraph`]: bdk_chain::indexed_tx_graph::IndexedTxGraph
127+
pub indexed_tx_graph:
128+
indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>,
129+
}
130+
131+
impl Append for ChangeSet {
132+
fn append(&mut self, other: Self) {
133+
Append::append(&mut self.chain, other.chain);
134+
Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
135+
}
136+
137+
fn is_empty(&self) -> bool {
138+
self.chain.is_empty() && self.indexed_tx_graph.is_empty()
139+
}
140+
}
141+
142+
impl From<local_chain::ChangeSet> for ChangeSet {
143+
fn from(chain: local_chain::ChangeSet) -> Self {
144+
Self {
145+
chain,
146+
..Default::default()
147+
}
148+
}
149+
}
100150

101-
/// The changeset produced internally by [`Wallet`] when mutated.
102-
pub type ChangeSet = WalletChangeSet<KeychainKind, ConfirmationTimeAnchor>;
151+
impl From<indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>>
152+
for ChangeSet
153+
{
154+
fn from(
155+
indexed_tx_graph: indexed_tx_graph::ChangeSet<
156+
ConfirmationTimeAnchor,
157+
keychain::ChangeSet<KeychainKind>,
158+
>,
159+
) -> Self {
160+
Self {
161+
indexed_tx_graph,
162+
..Default::default()
163+
}
164+
}
165+
}
103166

104167
/// The address index selection strategy to use to derived an address from the wallet's external
105168
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
@@ -1857,7 +1920,11 @@ impl<D> Wallet<D> {
18571920
where
18581921
D: PersistBackend<ChangeSet>,
18591922
{
1860-
let mut changeset = ChangeSet::from(self.chain.apply_update(update.chain)?);
1923+
let mut changeset = match update.chain {
1924+
Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
1925+
None => ChangeSet::default(),
1926+
};
1927+
18611928
let (_, index_changeset) = self
18621929
.indexed_graph
18631930
.index

crates/chain/src/indexed_tx_graph.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,18 @@ impl<A, K> From<keychain::ChangeSet<K>> for ChangeSet<A, keychain::ChangeSet<K>>
225225
}
226226
}
227227

228-
/// Represents a structure that can index transaction data.
228+
/// Utilities for indexing transaction data.
229+
///
230+
/// Types which implement this trait can be used to construct an [`IndexedTxGraph`].
231+
/// This trait's methods should rarely be called directly.
229232
pub trait Indexer {
230233
/// The resultant "changeset" when new transaction data is indexed.
231234
type ChangeSet;
232235

233236
/// Scan and index the given `outpoint` and `txout`.
234237
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet;
235238

236-
/// Scan and index the given transaction.
239+
/// Scans a transaction for relevant outpoints, which are stored and indexed internally.
237240
fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet;
238241

239242
/// Apply changeset to itself.

crates/chain/src/keychain.rs

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
//!
1111
//! [`SpkTxOutIndex`]: crate::SpkTxOutIndex
1212
13-
use crate::{
14-
collections::BTreeMap, indexed_tx_graph, local_chain, tx_graph::TxGraph, Anchor, Append,
15-
};
13+
use crate::{collections::BTreeMap, Append};
1614

1715
#[cfg(feature = "miniscript")]
1816
mod txout_index;
@@ -82,98 +80,6 @@ impl<K> AsRef<BTreeMap<K, u32>> for ChangeSet<K> {
8280
}
8381
}
8482

85-
/// A structure to update [`KeychainTxOutIndex`], [`TxGraph`] and [`LocalChain`] atomically.
86-
///
87-
/// [`LocalChain`]: local_chain::LocalChain
88-
#[derive(Debug, Clone)]
89-
pub struct WalletUpdate<K, A> {
90-
/// Contains the last active derivation indices per keychain (`K`), which is used to update the
91-
/// [`KeychainTxOutIndex`].
92-
pub last_active_indices: BTreeMap<K, u32>,
93-
94-
/// Update for the [`TxGraph`].
95-
pub graph: TxGraph<A>,
96-
97-
/// Update for the [`LocalChain`].
98-
///
99-
/// [`LocalChain`]: local_chain::LocalChain
100-
pub chain: local_chain::Update,
101-
}
102-
103-
impl<K, A> WalletUpdate<K, A> {
104-
/// Construct a [`WalletUpdate`] with a given [`local_chain::Update`].
105-
pub fn new(chain_update: local_chain::Update) -> Self {
106-
Self {
107-
last_active_indices: BTreeMap::new(),
108-
graph: TxGraph::default(),
109-
chain: chain_update,
110-
}
111-
}
112-
}
113-
114-
/// A structure that records the corresponding changes as result of applying an [`WalletUpdate`].
115-
#[derive(Debug, Clone, PartialEq)]
116-
#[cfg_attr(
117-
feature = "serde",
118-
derive(serde::Deserialize, serde::Serialize),
119-
serde(
120-
crate = "serde_crate",
121-
bound(
122-
deserialize = "K: Ord + serde::Deserialize<'de>, A: Ord + serde::Deserialize<'de>",
123-
serialize = "K: Ord + serde::Serialize, A: Ord + serde::Serialize",
124-
)
125-
)
126-
)]
127-
pub struct WalletChangeSet<K, A> {
128-
/// Changes to the [`LocalChain`].
129-
///
130-
/// [`LocalChain`]: local_chain::LocalChain
131-
pub chain: local_chain::ChangeSet,
132-
133-
/// ChangeSet to [`IndexedTxGraph`].
134-
///
135-
/// [`IndexedTxGraph`]: crate::indexed_tx_graph::IndexedTxGraph
136-
pub indexed_tx_graph: indexed_tx_graph::ChangeSet<A, ChangeSet<K>>,
137-
}
138-
139-
impl<K, A> Default for WalletChangeSet<K, A> {
140-
fn default() -> Self {
141-
Self {
142-
chain: Default::default(),
143-
indexed_tx_graph: Default::default(),
144-
}
145-
}
146-
}
147-
148-
impl<K: Ord, A: Anchor> Append for WalletChangeSet<K, A> {
149-
fn append(&mut self, other: Self) {
150-
Append::append(&mut self.chain, other.chain);
151-
Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
152-
}
153-
154-
fn is_empty(&self) -> bool {
155-
self.chain.is_empty() && self.indexed_tx_graph.is_empty()
156-
}
157-
}
158-
159-
impl<K, A> From<local_chain::ChangeSet> for WalletChangeSet<K, A> {
160-
fn from(chain: local_chain::ChangeSet) -> Self {
161-
Self {
162-
chain,
163-
..Default::default()
164-
}
165-
}
166-
}
167-
168-
impl<K, A> From<indexed_tx_graph::ChangeSet<A, ChangeSet<K>>> for WalletChangeSet<K, A> {
169-
fn from(indexed_tx_graph: indexed_tx_graph::ChangeSet<A, ChangeSet<K>>) -> Self {
170-
Self {
171-
indexed_tx_graph,
172-
..Default::default()
173-
}
174-
}
175-
}
176-
17783
/// Balance, differentiated into various categories.
17884
#[derive(Debug, PartialEq, Eq, Clone, Default)]
17985
#[cfg_attr(

crates/chain/src/keychain/txout_index.rs

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
indexed_tx_graph::Indexer,
44
miniscript::{Descriptor, DescriptorPublicKey},
55
spk_iter::BIP32_MAX_INDEX,
6-
ForEachTxOut, SpkIterator, SpkTxOutIndex,
6+
SpkIterator, SpkTxOutIndex,
77
};
88
use alloc::vec::Vec;
99
use bitcoin::{OutPoint, Script, TxOut};
@@ -91,11 +91,18 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
9191
type ChangeSet = super::ChangeSet<K>;
9292

9393
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
94-
self.scan_txout(outpoint, txout)
94+
match self.inner.scan_txout(outpoint, txout).cloned() {
95+
Some((keychain, index)) => self.reveal_to_target(&keychain, index).1,
96+
None => super::ChangeSet::default(),
97+
}
9598
}
9699

97100
fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::ChangeSet {
98-
self.scan(tx)
101+
let mut changeset = super::ChangeSet::<K>::default();
102+
for (op, txout) in tx.output.iter().enumerate() {
103+
changeset.append(self.index_txout(OutPoint::new(tx.txid(), op as u32), txout));
104+
}
105+
changeset
99106
}
100107

101108
fn initial_changeset(&self) -> Self::ChangeSet {
@@ -112,38 +119,6 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
112119
}
113120

114121
impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
115-
/// Scans an object for relevant outpoints, which are stored and indexed internally.
116-
///
117-
/// If the matched script pubkey is part of the lookahead, the last stored index is updated for
118-
/// the script pubkey's keychain and the [`super::ChangeSet`] returned will reflect the
119-
/// change.
120-
///
121-
/// Typically, this method is used in two situations:
122-
///
123-
/// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
124-
/// your txouts.
125-
/// 2. When getting new data from the chain, you usually scan it before incorporating it into
126-
/// your chain state (i.e., `SparseChain`, `ChainGraph`).
127-
///
128-
/// See [`ForEachTxout`] for the types that support this.
129-
///
130-
/// [`ForEachTxout`]: crate::ForEachTxOut
131-
pub fn scan(&mut self, txouts: &impl ForEachTxOut) -> super::ChangeSet<K> {
132-
let mut changeset = super::ChangeSet::<K>::default();
133-
txouts.for_each_txout(|(op, txout)| changeset.append(self.scan_txout(op, txout)));
134-
changeset
135-
}
136-
137-
/// Scan a single outpoint for a matching script pubkey.
138-
///
139-
/// If it matches, this will store and index it.
140-
pub fn scan_txout(&mut self, op: OutPoint, txout: &TxOut) -> super::ChangeSet<K> {
141-
match self.inner.scan_txout(op, txout).cloned() {
142-
Some((keychain, index)) => self.reveal_to_target(&keychain, index).1,
143-
None => super::ChangeSet::default(),
144-
}
145-
}
146-
147122
/// Return a reference to the internal [`SpkTxOutIndex`].
148123
pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
149124
&self.inner
@@ -199,15 +174,14 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
199174

200175
/// Set the lookahead count for `keychain`.
201176
///
202-
/// The lookahead is the number of scripts to cache ahead of the last stored script index. This
203-
/// is useful during a scan via [`scan`] or [`scan_txout`].
177+
/// The lookahead is the number of scripts to cache ahead of the last revealed script index. This
178+
/// is useful to find outputs you own when processing block data that lie beyond the last revealed
179+
/// index. In certain situations, such as when performing an initial scan of the blockchain during
180+
/// wallet import, it may be uncertain or unknown what the last revealed index is.
204181
///
205182
/// # Panics
206183
///
207184
/// This will panic if the `keychain` does not exist.
208-
///
209-
/// [`scan`]: Self::scan
210-
/// [`scan_txout`]: Self::scan_txout
211185
pub fn set_lookahead(&mut self, keychain: &K, lookahead: u32) {
212186
self.lookahead.insert(keychain.clone(), lookahead);
213187
self.replenish_lookahead(keychain);

0 commit comments

Comments
 (0)