Skip to content

Commit 1c593a3

Browse files
committed
Merge #1463: No descriptor ids in spk txout index
8dd1744 refactor(chain): compute txid once for `KeychainTxOutIndex::index_tx` (志宇) 639d735 refactor(chain): change field names to be more sane (志宇) 5a02f40 docs(chain): fix docs (志宇) c77e12b refactor(chain): `KeychainTxOutIndex` use `HashMap` for fields (志宇) 4d3846a chore(chain): s/replenish_lookahead/replenish_inner_index/ (LLFourn) 8779afd chore(chain): document insert_descriptor invariants better (LLFourn) 69f2a69 refactor(chain): improve replenish lookeahd internals (LLFourn) 5a584d0 chore(chain): Fix Indexed and KeychainIndexed documentaion (Lloyd Fournier) b8ba5a0 chore(chain): Improve documentation of keychain::ChangeSet (LLFourn) 101a09a chore(chain): Standardise KeychainTxOutIndex return types (LLFourn) bce070b chore(chain): add type IndexSpk, fix clippy type complexity warning (Steve Myers) 4d2442c chore(chain): misc docs and insert_descriptor fixes (LLFourn) bc2a8be refactor(keychain): Fix KeychainTxOutIndex range queries (LLFourn) 3b2ff0c Write failing test for keychain range querying (LLFourn) Pull request description: Fixes #1459 This reverts part of the changes in #1203. There the `SpkTxOutIndex<(K,u32)>` was changed to `SpkTxOutIndex<(DescriptorId, u32>)`. This led to a complicated translation logic in `KeychainTxOutIndex` (where the API is based on `K`) to transform calls to it to calls to the underlying `SpkTxOutIndex` (which now indexes by `DescriptorId`). The translation layer was broken when it came to translating range queries from the `KeychainTxOutIndex`. My solution was just to revert this part of the change and remove the need for a translation layer (almost) altogether. A thin translation layer remains to ensure that un-revealed spks are filtered out before being returned from the `KeychainTxOutIndex` methods. I feel like this PR could be extended to include a bunch of ergonomics improvements that are easier to implement now. But I think that's the point of #1451 so I held off and should probably go and scope creep that one instead. ### 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 #### Bugfixes: * [x] This pull request breaks the existing API * [x] I've added tests to reproduce the issue which are now passing * [x] I'm linking the issue being fixed by this PR ACKs for top commit: evanlinjin: ACK 8dd1744 Tree-SHA512: 283e6b6d4218902298e2e848fe847a6c85e27af4eee3e4337e3dad6eacf9beaa08ac99b1dce7b6fb199ca53931e543ea365728a81c41567a2e510cce77b12ac0
2 parents 3b040a7 + 8dd1744 commit 1c593a3

File tree

17 files changed

+643
-645
lines changed

17 files changed

+643
-645
lines changed

clippy.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
msrv="1.63.0"
1+
msrv="1.63.0"

crates/chain/src/keychain.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
1313
#[cfg(feature = "miniscript")]
1414
mod txout_index;
15-
use bitcoin::Amount;
15+
use bitcoin::{Amount, ScriptBuf};
1616
#[cfg(feature = "miniscript")]
1717
pub use txout_index::*;
1818

@@ -49,6 +49,11 @@ impl Balance {
4949
}
5050
}
5151

52+
/// A tuple of keychain index and `T` representing the indexed value.
53+
pub type Indexed<T> = (u32, T);
54+
/// A tuple of keychain `K`, derivation index (`u32`) and a `T` associated with them.
55+
pub type KeychainIndexed<K, T> = ((K, u32), T);
56+
5257
impl core::fmt::Display for Balance {
5358
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5459
write!(

crates/chain/src/keychain/txout_index.rs

Lines changed: 443 additions & 475 deletions
Large diffs are not rendered by default.

crates/chain/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub use chain_data::*;
2828
pub mod indexed_tx_graph;
2929
pub use indexed_tx_graph::IndexedTxGraph;
3030
pub mod keychain;
31+
pub use keychain::{Indexed, KeychainIndexed};
3132
pub mod local_chain;
3233
mod tx_data_traits;
3334
pub mod tx_graph;

crates/chain/src/spk_client.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Helper types for spk-based blockchain clients.
22
33
use crate::{
4-
collections::BTreeMap, local_chain::CheckPoint, ConfirmationTimeHeightAnchor, TxGraph,
4+
collections::BTreeMap, keychain::Indexed, local_chain::CheckPoint,
5+
ConfirmationTimeHeightAnchor, TxGraph,
56
};
67
use alloc::{boxed::Box, vec::Vec};
78
use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
@@ -166,7 +167,7 @@ impl SyncRequest {
166167
self.chain_spks(
167168
index
168169
.revealed_spks(spk_range)
169-
.map(|(_, _, spk)| spk.to_owned())
170+
.map(|(_, spk)| spk.to_owned())
170171
.collect::<Vec<_>>(),
171172
)
172173
}
@@ -195,7 +196,7 @@ pub struct FullScanRequest<K> {
195196
/// [`LocalChain::tip`]: crate::local_chain::LocalChain::tip
196197
pub chain_tip: CheckPoint,
197198
/// Iterators of script pubkeys indexed by the keychain index.
198-
pub spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = (u32, ScriptBuf)> + Send>>,
199+
pub spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
199200
}
200201

201202
impl<K: Ord + Clone> FullScanRequest<K> {
@@ -238,7 +239,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
238239
pub fn set_spks_for_keychain(
239240
mut self,
240241
keychain: K,
241-
spks: impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send + 'static>,
242+
spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
242243
) -> Self {
243244
self.spks_by_keychain
244245
.insert(keychain, Box::new(spks.into_iter()));
@@ -252,7 +253,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
252253
pub fn chain_spks_for_keychain(
253254
mut self,
254255
keychain: K,
255-
spks: impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send + 'static>,
256+
spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
256257
) -> Self {
257258
match self.spks_by_keychain.remove(&keychain) {
258259
// clippy here suggests to remove `into_iter` from `spks.into_iter()`, but doing so

crates/chain/src/spk_iter.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
bitcoin::{secp256k1::Secp256k1, ScriptBuf},
3+
keychain::Indexed,
34
miniscript::{Descriptor, DescriptorPublicKey},
45
};
56
use core::{borrow::Borrow, ops::Bound, ops::RangeBounds};
@@ -97,7 +98,7 @@ impl<D> Iterator for SpkIterator<D>
9798
where
9899
D: Borrow<Descriptor<DescriptorPublicKey>>,
99100
{
100-
type Item = (u32, ScriptBuf);
101+
type Item = Indexed<ScriptBuf>;
101102

102103
fn next(&mut self) -> Option<Self::Item> {
103104
// For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after.
@@ -158,8 +159,12 @@ mod test {
158159
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
159160
let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
160161

161-
let _ = txout_index.insert_descriptor(TestKeychain::External, external_descriptor.clone());
162-
let _ = txout_index.insert_descriptor(TestKeychain::Internal, internal_descriptor.clone());
162+
let _ = txout_index
163+
.insert_descriptor(TestKeychain::External, external_descriptor.clone())
164+
.unwrap();
165+
let _ = txout_index
166+
.insert_descriptor(TestKeychain::Internal, internal_descriptor.clone())
167+
.unwrap();
163168

164169
(txout_index, external_descriptor, internal_descriptor)
165170
}

crates/chain/src/spk_txout_index.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<I> Default for SpkTxOutIndex<I> {
5252
}
5353
}
5454

55-
impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
55+
impl<I: Clone + Ord + core::fmt::Debug> Indexer for SpkTxOutIndex<I> {
5656
type ChangeSet = ();
5757

5858
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
@@ -76,7 +76,7 @@ impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
7676
}
7777
}
7878

79-
impl<I: Clone + Ord> SpkTxOutIndex<I> {
79+
impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
8080
/// Scans a transaction's outputs for matching script pubkeys.
8181
///
8282
/// Typically, this is used in two situations:

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bdk_chain::{
1010
indexed_tx_graph::{self, IndexedTxGraph},
1111
keychain::{self, Balance, KeychainTxOutIndex},
1212
local_chain::LocalChain,
13-
tx_graph, ChainPosition, ConfirmationHeightAnchor, DescriptorExt,
13+
tx_graph, Append, ChainPosition, ConfirmationHeightAnchor, DescriptorExt,
1414
};
1515
use bitcoin::{
1616
secp256k1::Secp256k1, Amount, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut,
@@ -34,7 +34,10 @@ fn insert_relevant_txs() {
3434
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::new(
3535
KeychainTxOutIndex::new(10),
3636
);
37-
let _ = graph.index.insert_descriptor((), descriptor.clone());
37+
let _ = graph
38+
.index
39+
.insert_descriptor((), descriptor.clone())
40+
.unwrap();
3841

3942
let tx_a = Transaction {
4043
output: vec![
@@ -140,8 +143,16 @@ fn test_list_owned_txouts() {
140143
KeychainTxOutIndex::new(10),
141144
);
142145

143-
let _ = graph.index.insert_descriptor("keychain_1".into(), desc_1);
144-
let _ = graph.index.insert_descriptor("keychain_2".into(), desc_2);
146+
assert!(!graph
147+
.index
148+
.insert_descriptor("keychain_1".into(), desc_1)
149+
.unwrap()
150+
.is_empty());
151+
assert!(!graph
152+
.index
153+
.insert_descriptor("keychain_2".into(), desc_2)
154+
.unwrap()
155+
.is_empty());
145156

146157
// Get trusted and untrusted addresses
147158

@@ -257,18 +268,26 @@ fn test_list_owned_txouts() {
257268
.unwrap_or_else(|| panic!("block must exist at {}", height));
258269
let txouts = graph
259270
.graph()
260-
.filter_chain_txouts(&local_chain, chain_tip, graph.index.outpoints())
271+
.filter_chain_txouts(
272+
&local_chain,
273+
chain_tip,
274+
graph.index.outpoints().iter().cloned(),
275+
)
261276
.collect::<Vec<_>>();
262277

263278
let utxos = graph
264279
.graph()
265-
.filter_chain_unspents(&local_chain, chain_tip, graph.index.outpoints())
280+
.filter_chain_unspents(
281+
&local_chain,
282+
chain_tip,
283+
graph.index.outpoints().iter().cloned(),
284+
)
266285
.collect::<Vec<_>>();
267286

268287
let balance = graph.graph().balance(
269288
&local_chain,
270289
chain_tip,
271-
graph.index.outpoints(),
290+
graph.index.outpoints().iter().cloned(),
272291
|_, spk: &Script| trusted_spks.contains(&spk.to_owned()),
273292
);
274293

0 commit comments

Comments
 (0)