Skip to content

Commit adf7d0c

Browse files
committed
Merge #537: refactor wallet address caching into its own public method
edf2f0c refactor wallet address caching into its own public method for offline wallet use (a5an0) Pull request description: ### Description Currently, the only way to ensure that a wallet's internal database has addresses loaded and cached is through `Wallet::sync`: that function generates and caches up to a number of addresses if they aren't already in the database, and then uses the wallet's blockchain client to sync those addresses. If you are using an offline wallet, there is no mechanism to ensure that the database has addresses loaded. This is a problem for usecases like an offline wallet being used as a multisig signer and wanting to validate change addresses as `Wallet::is_mine` will only work properly if the owned-address is loaded in the database. This PR takes the address caching functionality out of `Wallet::sync` and puts it in a new public method that is available to offline wallets. `Wallet::sync` uses this method internally. ### 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 * [X] I've added docs for the new feature * [X] I've updated `CHANGELOG.md` Top commit has no ACKs. Tree-SHA512: 8ddc58d71457163bb20ff663ac508feb4e77000688161b63841a94db30b3f29f60f35fa2467bd99546123148873e3aed11e2f13ae6cbceda6605b83c227d9079
2 parents f0188f4 + 4291f84 commit adf7d0c

File tree

2 files changed

+47
-35
lines changed

2 files changed

+47
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
1010
- `verify` flag removed from `TransactionDetails`.
1111
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
12+
- added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database
1213

1314
## [v0.16.1] - [v0.16.0]
1415

src/wallet/mod.rs

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,50 @@ where
337337
}
338338
}
339339

340+
/// Ensures that there are at least `max_addresses` addresses cached in the database if the
341+
/// descriptor is derivable, or 1 address if it is not.
342+
/// Will return `Ok(true)` if there are new addresses generated (either external or internal),
343+
/// and `Ok(false)` if all the required addresses are already cached. This function is useful to
344+
/// explicitly cache addresses in a wallet to do things like check [`Wallet::is_mine`] on
345+
/// transaction output scripts.
346+
pub fn ensure_addresses_cached(&self, max_addresses: u32) -> Result<bool, Error> {
347+
let mut new_addresses_cached = false;
348+
let max_address = match self.descriptor.is_deriveable() {
349+
false => 0,
350+
true => max_addresses,
351+
};
352+
debug!("max_address {}", max_address);
353+
if self
354+
.database
355+
.borrow()
356+
.get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))?
357+
.is_none()
358+
{
359+
debug!("caching external addresses");
360+
new_addresses_cached = true;
361+
self.cache_addresses(KeychainKind::External, 0, max_address)?;
362+
}
363+
364+
if let Some(change_descriptor) = &self.change_descriptor {
365+
let max_address = match change_descriptor.is_deriveable() {
366+
false => 0,
367+
true => max_addresses,
368+
};
369+
370+
if self
371+
.database
372+
.borrow()
373+
.get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))?
374+
.is_none()
375+
{
376+
debug!("caching internal addresses");
377+
new_addresses_cached = true;
378+
self.cache_addresses(KeychainKind::Internal, 0, max_address)?;
379+
}
380+
}
381+
Ok(new_addresses_cached)
382+
}
383+
340384
/// Return whether or not a `script` is part of this wallet (either internal or external)
341385
pub fn is_mine(&self, script: &Script) -> Result<bool, Error> {
342386
self.database.borrow().is_mine(script)
@@ -1504,41 +1548,8 @@ where
15041548
) -> Result<(), Error> {
15051549
debug!("Begin sync...");
15061550

1507-
let mut run_setup = false;
1508-
1509-
let max_address = match self.descriptor.is_deriveable() {
1510-
false => 0,
1511-
true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
1512-
};
1513-
debug!("max_address {}", max_address);
1514-
if self
1515-
.database
1516-
.borrow()
1517-
.get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))?
1518-
.is_none()
1519-
{
1520-
debug!("caching external addresses");
1521-
run_setup = true;
1522-
self.cache_addresses(KeychainKind::External, 0, max_address)?;
1523-
}
1524-
1525-
if let Some(change_descriptor) = &self.change_descriptor {
1526-
let max_address = match change_descriptor.is_deriveable() {
1527-
false => 0,
1528-
true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
1529-
};
1530-
1531-
if self
1532-
.database
1533-
.borrow()
1534-
.get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))?
1535-
.is_none()
1536-
{
1537-
debug!("caching internal addresses");
1538-
run_setup = true;
1539-
self.cache_addresses(KeychainKind::Internal, 0, max_address)?;
1540-
}
1541-
}
1551+
let run_setup =
1552+
self.ensure_addresses_cached(max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE))?;
15421553

15431554
debug!("run_setup: {}", run_setup);
15441555
// TODO: what if i generate an address first and cache some addresses?

0 commit comments

Comments
 (0)