Skip to content

Commit dbad84a

Browse files
committed
Persist cached SPKs on wallet creation
1 parent 4e202c8 commit dbad84a

2 files changed

Lines changed: 87 additions & 54 deletions

File tree

src/wallet/mod.rs

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,15 @@ impl Wallet {
355355
..Default::default()
356356
};
357357

358-
let tx_graph = make_indexed_graph(
359-
&mut stage,
360-
Default::default(),
358+
let index = make_keychain_index(
361359
Default::default(),
362360
descriptor,
363361
change_descriptor,
364362
params.lookahead,
365363
params.use_spk_cache,
366364
)?;
365+
stage.indexer.merge(index.initial_changeset());
366+
let tx_graph = IndexedTxGraph::new(index);
367367

368368
Ok(Wallet {
369369
signers,
@@ -559,10 +559,9 @@ impl Wallet {
559559
.map(|(op, _)| op)
560560
.collect();
561561

562-
let mut stage = ChangeSet::default();
562+
let stage = ChangeSet::default();
563563

564-
let tx_graph = make_indexed_graph(
565-
&mut stage,
564+
let tx_graph = make_loaded_indexed_graph(
566565
changeset.tx_graph,
567566
changeset.indexer,
568567
descriptor,
@@ -2828,8 +2827,50 @@ fn new_local_utxo(
28282827
}
28292828
}
28302829

2831-
fn make_indexed_graph(
2832-
stage: &mut ChangeSet,
2830+
fn make_keychain_index(
2831+
indexer_changeset: chain::keychain_txout::ChangeSet,
2832+
descriptor: ExtendedDescriptor,
2833+
change_descriptor: Option<ExtendedDescriptor>,
2834+
lookahead: u32,
2835+
use_spk_cache: bool,
2836+
) -> Result<KeychainTxOutIndex<KeychainKind>, DescriptorError> {
2837+
let mut idx = KeychainTxOutIndex::from_changeset(lookahead, use_spk_cache, indexer_changeset);
2838+
2839+
let descriptor_inserted = idx
2840+
.insert_descriptor(KeychainKind::External, descriptor)
2841+
.expect("already checked to be a unique, wildcard, non-multipath descriptor");
2842+
assert!(
2843+
descriptor_inserted,
2844+
"this must be the first time we are seeing this descriptor"
2845+
);
2846+
2847+
let change_descriptor = match change_descriptor {
2848+
Some(change_descriptor) => change_descriptor,
2849+
None => return Ok(idx),
2850+
};
2851+
2852+
let change_descriptor_inserted = idx
2853+
.insert_descriptor(KeychainKind::Internal, change_descriptor)
2854+
.map_err(|e| {
2855+
use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2856+
match e {
2857+
InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2858+
crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2859+
}
2860+
InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2861+
unreachable!("this is the first time we're assigning internal")
2862+
}
2863+
}
2864+
})?;
2865+
assert!(
2866+
change_descriptor_inserted,
2867+
"this must be the first time we are seeing this descriptor"
2868+
);
2869+
2870+
Ok(idx)
2871+
}
2872+
2873+
fn make_loaded_indexed_graph(
28332874
tx_graph_changeset: chain::tx_graph::ChangeSet<ConfirmationBlockTime>,
28342875
indexer_changeset: chain::keychain_txout::ChangeSet,
28352876
descriptor: ExtendedDescriptor,
@@ -2838,50 +2879,25 @@ fn make_indexed_graph(
28382879
use_spk_cache: bool,
28392880
) -> Result<IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>, DescriptorError>
28402881
{
2841-
let (indexed_graph, changeset) = IndexedTxGraph::from_changeset(
2882+
let (indexed_graph, reindex_changeset) = IndexedTxGraph::from_changeset(
28422883
chain::indexed_tx_graph::ChangeSet {
28432884
tx_graph: tx_graph_changeset,
28442885
indexer: indexer_changeset,
28452886
},
2846-
|idx_cs| -> Result<KeychainTxOutIndex<KeychainKind>, DescriptorError> {
2847-
let mut idx = KeychainTxOutIndex::from_changeset(lookahead, use_spk_cache, idx_cs);
2848-
2849-
let descriptor_inserted = idx
2850-
.insert_descriptor(KeychainKind::External, descriptor)
2851-
.expect("already checked to be a unique, wildcard, non-multipath descriptor");
2852-
assert!(
2853-
descriptor_inserted,
2854-
"this must be the first time we are seeing this descriptor"
2855-
);
2856-
2857-
let change_descriptor = match change_descriptor {
2858-
Some(change_descriptor) => change_descriptor,
2859-
None => return Ok(idx),
2860-
};
2861-
2862-
let change_descriptor_inserted = idx
2863-
.insert_descriptor(KeychainKind::Internal, change_descriptor)
2864-
.map_err(|e| {
2865-
use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2866-
match e {
2867-
InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2868-
crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2869-
}
2870-
InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2871-
unreachable!("this is the first time we're assigning internal")
2872-
}
2873-
}
2874-
})?;
2875-
assert!(
2876-
change_descriptor_inserted,
2877-
"this must be the first time we are seeing this descriptor"
2878-
);
2879-
2880-
Ok(idx)
2887+
|idx_cs| {
2888+
make_keychain_index(
2889+
idx_cs,
2890+
descriptor,
2891+
change_descriptor,
2892+
lookahead,
2893+
use_spk_cache,
2894+
)
28812895
},
28822896
)?;
2883-
stage.tx_graph.merge(changeset.tx_graph);
2884-
stage.indexer.merge(changeset.indexer);
2897+
debug_assert!(
2898+
reindex_changeset.is_empty(),
2899+
"loading a wallet should not stage new changes"
2900+
);
28852901
Ok(indexed_graph)
28862902
}
28872903

tests/persisted_wallet.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,39 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
101101
let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
102102

103103
// create new wallet
104-
let wallet_spk_index = {
104+
{
105105
let mut db = create_db(&file_path)?;
106-
let mut wallet = Wallet::create(external_desc, internal_desc)
106+
let wallet = Wallet::create(external_desc, internal_desc)
107107
.network(Network::Testnet)
108108
.use_spk_cache(true)
109109
.create_wallet(&mut db)?;
110110

111-
wallet.reveal_next_address(KeychainKind::External);
111+
assert!(
112+
wallet.staged().is_none(),
113+
"persisted wallet creation should write the initial changeset"
114+
);
115+
}
116+
117+
// reload immediately to ensure the initial cached SPKs were persisted at creation time
118+
let wallet_spk_index = {
119+
let mut db = open_db(&file_path)?;
120+
let mut wallet = Wallet::load()
121+
.check_network(Network::Testnet)
122+
.use_spk_cache(true)
123+
.load_wallet(&mut db)?
124+
.expect("wallet must exist");
125+
126+
assert!(wallet.staged().is_none());
127+
128+
let revealed_external_addr = wallet.reveal_next_address(KeychainKind::External);
112129

113130
check_cache_cs(
114131
&staged_cache(&wallet),
115-
[
116-
(KeychainKind::External, 0..DEFAULT_LOOKAHEAD + 1),
117-
(KeychainKind::Internal, 0..DEFAULT_LOOKAHEAD),
118-
],
119-
"cache cs must return initial set + the external index that was just derived",
132+
[(
133+
KeychainKind::External,
134+
[revealed_external_addr.index + DEFAULT_LOOKAHEAD],
135+
)],
136+
"initial cached SPKs should already be persisted at wallet creation",
120137
);
121138

122139
// persist new wallet changes

0 commit comments

Comments
 (0)