Skip to content

Commit 9077c07

Browse files
committed
chain: add a 'lookahead' parameter to add_keychain
The wallet is currently created without setting any lookahead value for the keychain. This implicitly makes it a lookahead of 0. As this is a high-level interface we should avoid footguns and aim for a reasonable default. Instead of simply patching it for the wallet, make the interface foolproof by requiring any caller of `add_keychain` to set a lookahead value. (h/t evanlinjin for the suggestion.)
1 parent 55b680c commit 9077c07

File tree

4 files changed

+67
-31
lines changed

4 files changed

+67
-31
lines changed

crates/chain/src/keychain/txout_index.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use core::{fmt::Debug, ops::Deref};
1111

1212
use crate::Append;
1313

14+
const DEFAULT_LOOKAHEAD: u32 = 1_000;
15+
1416
/// A convenient wrapper around [`SpkTxOutIndex`] that relates script pubkeys to miniscript public
1517
/// [`Descriptor`]s.
1618
///
@@ -46,7 +48,7 @@ use crate::Append;
4648
/// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
4749
/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
4850
/// # let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
49-
/// # let descriptor_for_user_42 = external_descriptor.clone();
51+
/// # let (descriptor_for_user_42, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/2/*)").unwrap();
5052
/// txout_index.add_keychain(MyKeychain::External, external_descriptor);
5153
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor);
5254
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42);
@@ -134,23 +136,48 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
134136
&self.keychains
135137
}
136138

139+
/// Add a keychain to the tracker's `txout_index` with a descriptor to derive addresses and a
140+
/// default lookahead of `1_000`.
141+
///
142+
/// See [`add_keychain_with_lookahead`] for details.
143+
///
144+
/// # Panics
145+
///
146+
/// This will panic if a different `descriptor` is introduced to the same `keychain`.
147+
///
148+
/// [`add_keychain_with_lookahead`]: Self::add_keychain_with_lookahead
149+
pub fn add_keychain(&mut self, keychain: K, descriptor: Descriptor<DescriptorPublicKey>) {
150+
self.add_keychain_with_lookahead(keychain, descriptor, DEFAULT_LOOKAHEAD)
151+
}
152+
137153
/// Add a keychain to the tracker's `txout_index` with a descriptor to derive addresses.
138154
///
139155
/// Adding a keychain means you will be able to derive new script pubkeys under that keychain
140156
/// and the txout index will discover transaction outputs with those script pubkeys.
141157
///
158+
/// Refer to [`set_lookahead`] for an explanation of the `lookahead` parameter.
159+
///
142160
/// # Panics
143161
///
144162
/// This will panic if a different `descriptor` is introduced to the same `keychain`.
145-
pub fn add_keychain(&mut self, keychain: K, descriptor: Descriptor<DescriptorPublicKey>) {
163+
///
164+
/// [`set_lookahead`]: Self::set_lookahead
165+
pub fn add_keychain_with_lookahead(
166+
&mut self,
167+
keychain: K,
168+
descriptor: Descriptor<DescriptorPublicKey>,
169+
lookahead: u32,
170+
) {
146171
let old_descriptor = &*self
147172
.keychains
148-
.entry(keychain)
173+
.entry(keychain.clone())
149174
.or_insert_with(|| descriptor.clone());
150175
assert_eq!(
151176
&descriptor, old_descriptor,
152177
"keychain already contains a different descriptor"
153178
);
179+
self.lookahead.insert(keychain.clone(), lookahead);
180+
self.replenish_lookahead(&keychain);
154181
}
155182

156183
/// Return the lookahead setting for each keychain.
@@ -390,10 +417,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
390417
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
391418
let lookahead = self.lookahead.get(keychain).map_or(0, |v| *v);
392419

393-
debug_assert_eq!(
394-
next_reveal_index + lookahead,
395-
self.next_store_index(keychain)
396-
);
420+
debug_assert!(next_reveal_index + lookahead >= self.next_store_index(keychain));
397421

398422
// if we need to reveal new indices, the latest revealed index goes here
399423
let mut reveal_to_index = None;

crates/chain/src/spk_iter.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,16 @@ mod test {
154154
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
155155
let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
156156

157-
txout_index.add_keychain(TestKeychain::External, external_descriptor.clone());
158-
txout_index.add_keychain(TestKeychain::Internal, internal_descriptor.clone());
157+
txout_index.add_keychain_with_lookahead(
158+
TestKeychain::External,
159+
external_descriptor.clone(),
160+
0,
161+
);
162+
txout_index.add_keychain_with_lookahead(
163+
TestKeychain::Internal,
164+
internal_descriptor.clone(),
165+
0,
166+
);
159167

160168
(txout_index, external_descriptor, internal_descriptor)
161169
}

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ fn insert_relevant_txs() {
2828
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
2929

3030
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::default();
31-
graph.index.add_keychain((), descriptor);
32-
graph.index.set_lookahead(&(), 10);
31+
graph.index.add_keychain_with_lookahead((), descriptor, 10);
3332

3433
let tx_a = Transaction {
3534
output: vec![
@@ -121,9 +120,12 @@ fn test_list_owned_txouts() {
121120
let mut graph =
122121
IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>::default();
123122

124-
graph.index.add_keychain("keychain_1".into(), desc_1);
125-
graph.index.add_keychain("keychain_2".into(), desc_2);
126-
graph.index.set_lookahead_for_all(10);
123+
graph
124+
.index
125+
.add_keychain_with_lookahead("keychain_1".into(), desc_1, 10);
126+
graph
127+
.index
128+
.add_keychain_with_lookahead("keychain_2".into(), desc_2, 10);
127129

128130
// Get trusted and untrusted addresses
129131

crates/chain/tests/test_keychain_txout_index.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ enum TestKeychain {
1818
Internal,
1919
}
2020

21-
fn init_txout_index() -> (
21+
fn init_txout_index(
22+
internal_lookahead: u32,
23+
external_lookahead: u32,
24+
) -> (
2225
bdk_chain::keychain::KeychainTxOutIndex<TestKeychain>,
2326
Descriptor<DescriptorPublicKey>,
2427
Descriptor<DescriptorPublicKey>,
@@ -29,8 +32,16 @@ fn init_txout_index() -> (
2932
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
3033
let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
3134

32-
txout_index.add_keychain(TestKeychain::External, external_descriptor.clone());
33-
txout_index.add_keychain(TestKeychain::Internal, internal_descriptor.clone());
35+
txout_index.add_keychain_with_lookahead(
36+
TestKeychain::External,
37+
external_descriptor.clone(),
38+
internal_lookahead,
39+
);
40+
txout_index.add_keychain_with_lookahead(
41+
TestKeychain::Internal,
42+
internal_descriptor.clone(),
43+
external_lookahead,
44+
);
3445

3546
(txout_index, external_descriptor, internal_descriptor)
3647
}
@@ -46,7 +57,7 @@ fn spk_at_index(descriptor: &Descriptor<DescriptorPublicKey>, index: u32) -> Scr
4657
fn test_set_all_derivation_indices() {
4758
use bdk_chain::indexed_tx_graph::Indexer;
4859

49-
let (mut txout_index, _, _) = init_txout_index();
60+
let (mut txout_index, _, _) = init_txout_index(0, 0);
5061
let derive_to: BTreeMap<_, _> =
5162
[(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
5263
assert_eq!(
@@ -64,15 +75,7 @@ fn test_set_all_derivation_indices() {
6475

6576
#[test]
6677
fn test_lookahead() {
67-
let (mut txout_index, external_desc, internal_desc) = init_txout_index();
68-
69-
// ensure it does not break anything if lookahead is set multiple times
70-
(0..=10).for_each(|lookahead| txout_index.set_lookahead(&TestKeychain::External, lookahead));
71-
(0..=20)
72-
.filter(|v| v % 2 == 0)
73-
.for_each(|lookahead| txout_index.set_lookahead(&TestKeychain::Internal, lookahead));
74-
75-
assert_eq!(txout_index.inner().all_spks().len(), 30);
78+
let (mut txout_index, external_desc, internal_desc) = init_txout_index(10, 20);
7679

7780
// given:
7881
// - external lookahead set to 10
@@ -226,8 +229,7 @@ fn test_lookahead() {
226229
// - last used index should change as expected
227230
#[test]
228231
fn test_scan_with_lookahead() {
229-
let (mut txout_index, external_desc, _) = init_txout_index();
230-
txout_index.set_lookahead_for_all(10);
232+
let (mut txout_index, external_desc, _) = init_txout_index(10, 10);
231233

232234
let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
233235
.into_iter()
@@ -281,7 +283,7 @@ fn test_scan_with_lookahead() {
281283
#[test]
282284
#[rustfmt::skip]
283285
fn test_wildcard_derivations() {
284-
let (mut txout_index, external_desc, _) = init_txout_index();
286+
let (mut txout_index, external_desc, _) = init_txout_index(0, 0);
285287
let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
286288
let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
287289
let external_spk_26 = external_desc.at_derivation_index(26).unwrap().script_pubkey();
@@ -348,7 +350,7 @@ fn test_non_wildcard_derivations() {
348350
.unwrap()
349351
.script_pubkey();
350352

351-
txout_index.add_keychain(TestKeychain::External, no_wildcard_descriptor);
353+
txout_index.add_keychain_with_lookahead(TestKeychain::External, no_wildcard_descriptor, 0);
352354

353355
// given:
354356
// - `txout_index` with no stored scripts

0 commit comments

Comments
 (0)