Skip to content

Commit 6f2cb9c

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 6f2cb9c

File tree

6 files changed

+45
-37
lines changed

6 files changed

+45
-37
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ use crate::wallet::error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError
7575

7676
const COINBASE_MATURITY: u32 = 100;
7777

78+
const DEFAULT_LOOKAHEAD: u32 = 1_000;
79+
7880
/// A Bitcoin wallet
7981
///
8082
/// The `Wallet` struct acts as a way of coherently interfacing with output descriptors and related transactions.
@@ -2375,13 +2377,13 @@ fn create_signers<E: IntoWalletDescriptor>(
23752377
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), crate::descriptor::error::Error> {
23762378
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
23772379
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
2378-
index.add_keychain(KeychainKind::External, descriptor);
2380+
index.add_keychain(KeychainKind::External, descriptor, DEFAULT_LOOKAHEAD);
23792381

23802382
let change_signers = match change_descriptor {
23812383
Some(descriptor) => {
23822384
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
23832385
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
2384-
index.add_keychain(KeychainKind::Internal, descriptor);
2386+
index.add_keychain(KeychainKind::Internal, descriptor, DEFAULT_LOOKAHEAD);
23852387
signers
23862388
}
23872389
None => Arc::new(SignersContainer::new()),

crates/chain/src/keychain/txout_index.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ use crate::Append;
4747
/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
4848
/// # let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
4949
/// # let descriptor_for_user_42 = external_descriptor.clone();
50-
/// txout_index.add_keychain(MyKeychain::External, external_descriptor);
51-
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor);
52-
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42);
50+
/// txout_index.add_keychain(MyKeychain::External, external_descriptor, 0);
51+
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor, 0);
52+
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42, 0);
5353
///
5454
/// let new_spk_for_user = txout_index.reveal_next_spk(&MyKeychain::MyAppUser{ user_id: 42 });
5555
/// ```
@@ -142,15 +142,22 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
142142
/// # Panics
143143
///
144144
/// 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>) {
145+
pub fn add_keychain(
146+
&mut self,
147+
keychain: K,
148+
descriptor: Descriptor<DescriptorPublicKey>,
149+
lookahead: u32,
150+
) {
146151
let old_descriptor = &*self
147152
.keychains
148-
.entry(keychain)
153+
.entry(keychain.clone())
149154
.or_insert_with(|| descriptor.clone());
150155
assert_eq!(
151156
&descriptor, old_descriptor,
152157
"keychain already contains a different descriptor"
153158
);
159+
self.lookahead.insert(keychain.clone(), lookahead);
160+
self.replenish_lookahead(&keychain);
154161
}
155162

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

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

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

crates/chain/src/spk_iter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ 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(TestKeychain::External, external_descriptor.clone(), 0);
158+
txout_index.add_keychain(TestKeychain::Internal, internal_descriptor.clone(), 0);
159159

160160
(txout_index, external_descriptor, internal_descriptor)
161161
}

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 3 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((), descriptor, 10);
3332

3433
let tx_a = Transaction {
3534
output: vec![
@@ -121,9 +120,8 @@ 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.index.add_keychain("keychain_1".into(), desc_1, 10);
124+
graph.index.add_keychain("keychain_2".into(), desc_2, 10);
127125

128126
// Get trusted and untrusted addresses
129127

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(
36+
TestKeychain::External,
37+
external_descriptor.clone(),
38+
internal_lookahead,
39+
);
40+
txout_index.add_keychain(
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(TestKeychain::External, no_wildcard_descriptor, 0);
352354

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

example-crates/example_cli/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub type KeychainChangeSet<A> = (
3131
);
3232
pub type Database<'m, C> = Persist<Store<'m, C>, C>;
3333

34+
const DEFAULT_LOOKAHEAD: u32 = 1_000;
35+
3436
#[derive(Parser)]
3537
#[clap(author, version, about, long_about = None)]
3638
#[clap(propagate_version = true)]
@@ -669,7 +671,7 @@ where
669671

670672
let (descriptor, mut keymap) =
671673
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &args.descriptor)?;
672-
index.add_keychain(Keychain::External, descriptor);
674+
index.add_keychain(Keychain::External, descriptor, DEFAULT_LOOKAHEAD);
673675

674676
if let Some((internal_descriptor, internal_keymap)) = args
675677
.change_descriptor
@@ -678,7 +680,7 @@ where
678680
.transpose()?
679681
{
680682
keymap.extend(internal_keymap);
681-
index.add_keychain(Keychain::Internal, internal_descriptor);
683+
index.add_keychain(Keychain::Internal, internal_descriptor, DEFAULT_LOOKAHEAD);
682684
}
683685

684686
let mut db_backend = match Store::<'m, C>::open_or_create_new(db_magic, &args.db_path) {

0 commit comments

Comments
 (0)