Skip to content

Commit d03aa85

Browse files
committed
Merge branch 'master' into remove-blockchain-from-wallet
2 parents fbb50ad + adf7d0c commit d03aa85

File tree

3 files changed

+136
-63
lines changed

3 files changed

+136
-63
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

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`.
11+
- 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
1113

1214
### Sync API change
1315

src/testutils/blockchain_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ macro_rules! bdk_blockchain_tests {
817817

818818
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
819819
builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
820-
let (mut new_psbt, new_details) = builder.finish().unwrap();
820+
let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx");
821821
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
822822
assert!(finalized, "Cannot finalize transaction");
823823
blockchain.broadcast(&new_psbt.extract_tx()).unwrap();

src/wallet/mod.rs

Lines changed: 133 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,11 @@ where
236236
}
237237

238238
// Return a newly derived address using the external descriptor
239-
fn get_new_address(&self) -> Result<AddressInfo, Error> {
240-
let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
239+
fn get_new_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
240+
let incremented_index = self.fetch_and_increment_index(keychain)?;
241241

242242
let address_result = self
243-
.descriptor
243+
.get_descriptor_for_keychain(keychain)
244244
.as_derived(incremented_index, &self.secp)
245245
.address(self.network);
246246

@@ -252,12 +252,14 @@ where
252252
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
253253
}
254254

255-
// Return the the last previously derived address if it has not been used in a received
256-
// transaction. Otherwise return a new address using [`Wallet::get_new_address`].
257-
fn get_unused_address(&self) -> Result<AddressInfo, Error> {
258-
let current_index = self.fetch_index(KeychainKind::External)?;
255+
// Return the the last previously derived address for `keychain` if it has not been used in a
256+
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
257+
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
258+
let current_index = self.fetch_index(keychain)?;
259259

260-
let derived_key = self.descriptor.as_derived(current_index, &self.secp);
260+
let derived_key = self
261+
.get_descriptor_for_keychain(keychain)
262+
.as_derived(current_index, &self.secp);
261263

262264
let script_pubkey = derived_key.script_pubkey();
263265

@@ -269,7 +271,7 @@ where
269271
.any(|o| o.script_pubkey == script_pubkey);
270272

271273
if found_used {
272-
self.get_new_address()
274+
self.get_new_address(keychain)
273275
} else {
274276
derived_key
275277
.address(self.network)
@@ -281,21 +283,21 @@ where
281283
}
282284
}
283285

284-
// Return derived address for the external descriptor at a specific index
285-
fn peek_address(&self, index: u32) -> Result<AddressInfo, Error> {
286-
self.descriptor
286+
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
287+
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
288+
self.get_descriptor_for_keychain(keychain)
287289
.as_derived(index, &self.secp)
288290
.address(self.network)
289291
.map(|address| AddressInfo { index, address })
290292
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
291293
}
292294

293-
// Return derived address for the external descriptor at a specific index and reset current
295+
// Return derived address for `keychain` at a specific index and reset current
294296
// address index
295-
fn reset_address(&self, index: u32) -> Result<AddressInfo, Error> {
296-
self.set_index(KeychainKind::External, index)?;
297+
fn reset_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
298+
self.set_index(keychain, index)?;
297299

298-
self.descriptor
300+
self.get_descriptor_for_keychain(keychain)
299301
.as_derived(index, &self.secp)
300302
.address(self.network)
301303
.map(|address| AddressInfo { index, address })
@@ -306,12 +308,75 @@ where
306308
/// available address index selection strategies. If none of the keys in the descriptor are derivable
307309
/// (ie. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
308310
pub fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
311+
self._get_address(address_index, KeychainKind::External)
312+
}
313+
314+
/// Return a derived address using the internal (change) descriptor.
315+
///
316+
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
317+
///
318+
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
319+
/// in the descriptor are derivable (ie. does not end with /*) then the same address will always
320+
/// be returned for any [`AddressIndex`].
321+
pub fn get_internal_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
322+
self._get_address(address_index, KeychainKind::Internal)
323+
}
324+
325+
fn _get_address(
326+
&self,
327+
address_index: AddressIndex,
328+
keychain: KeychainKind,
329+
) -> Result<AddressInfo, Error> {
309330
match address_index {
310-
AddressIndex::New => self.get_new_address(),
311-
AddressIndex::LastUnused => self.get_unused_address(),
312-
AddressIndex::Peek(index) => self.peek_address(index),
313-
AddressIndex::Reset(index) => self.reset_address(index),
331+
AddressIndex::New => self.get_new_address(keychain),
332+
AddressIndex::LastUnused => self.get_unused_address(keychain),
333+
AddressIndex::Peek(index) => self.peek_address(index, keychain),
334+
AddressIndex::Reset(index) => self.reset_address(index, keychain),
335+
}
336+
}
337+
338+
/// Ensures that there are at least `max_addresses` addresses cached in the database if the
339+
/// descriptor is derivable, or 1 address if it is not.
340+
/// Will return `Ok(true)` if there are new addresses generated (either external or internal),
341+
/// and `Ok(false)` if all the required addresses are already cached. This function is useful to
342+
/// explicitly cache addresses in a wallet to do things like check [`Wallet::is_mine`] on
343+
/// transaction output scripts.
344+
pub fn ensure_addresses_cached(&self, max_addresses: u32) -> Result<bool, Error> {
345+
let mut new_addresses_cached = false;
346+
let max_address = match self.descriptor.is_deriveable() {
347+
false => 0,
348+
true => max_addresses,
349+
};
350+
debug!("max_address {}", max_address);
351+
if self
352+
.database
353+
.borrow()
354+
.get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))?
355+
.is_none()
356+
{
357+
debug!("caching external addresses");
358+
new_addresses_cached = true;
359+
self.cache_addresses(KeychainKind::External, 0, max_address)?;
314360
}
361+
362+
if let Some(change_descriptor) = &self.change_descriptor {
363+
let max_address = match change_descriptor.is_deriveable() {
364+
false => 0,
365+
true => max_addresses,
366+
};
367+
368+
if self
369+
.database
370+
.borrow()
371+
.get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))?
372+
.is_none()
373+
{
374+
debug!("caching internal addresses");
375+
new_addresses_cached = true;
376+
self.cache_addresses(KeychainKind::Internal, 0, max_address)?;
377+
}
378+
}
379+
Ok(new_addresses_cached)
315380
}
316381

317382
/// Return whether or not a `script` is part of this wallet (either internal or external)
@@ -660,7 +725,10 @@ where
660725
let mut drain_output = {
661726
let script_pubkey = match params.drain_to {
662727
Some(ref drain_recipient) => drain_recipient.clone(),
663-
None => self.get_change_address()?,
728+
None => self
729+
.get_internal_address(AddressIndex::New)?
730+
.address
731+
.script_pubkey(),
664732
};
665733

666734
TxOut {
@@ -1093,13 +1161,6 @@ where
10931161
.map(|(desc, child)| desc.as_derived(child, &self.secp)))
10941162
}
10951163

1096-
fn get_change_address(&self) -> Result<Script, Error> {
1097-
let (desc, keychain) = self._get_descriptor_for_keychain(KeychainKind::Internal);
1098-
let index = self.fetch_and_increment_index(keychain)?;
1099-
1100-
Ok(desc.as_derived(index, &self.secp).script_pubkey())
1101-
}
1102-
11031164
fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
11041165
let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
11051166
let index = match descriptor.is_deriveable() {
@@ -1463,46 +1524,14 @@ where
14631524
) -> Result<(), Error> {
14641525
debug!("Begin sync...");
14651526

1466-
let mut run_setup = false;
14671527
let SyncOptions {
14681528
max_addresses,
14691529
progress,
14701530
} = sync_opts;
14711531
let progress = progress.unwrap_or_else(|| Box::new(NoopProgress));
14721532

1473-
let max_address = match self.descriptor.is_deriveable() {
1474-
false => 0,
1475-
true => max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE),
1476-
};
1477-
debug!("max_address {}", max_address);
1478-
if self
1479-
.database
1480-
.borrow()
1481-
.get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))?
1482-
.is_none()
1483-
{
1484-
debug!("caching external addresses");
1485-
run_setup = true;
1486-
self.cache_addresses(KeychainKind::External, 0, max_address)?;
1487-
}
1488-
1489-
if let Some(change_descriptor) = &self.change_descriptor {
1490-
let max_address = match change_descriptor.is_deriveable() {
1491-
false => 0,
1492-
true => max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE),
1493-
};
1494-
1495-
if self
1496-
.database
1497-
.borrow()
1498-
.get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))?
1499-
.is_none()
1500-
{
1501-
debug!("caching internal addresses");
1502-
run_setup = true;
1503-
self.cache_addresses(KeychainKind::Internal, 0, max_address)?;
1504-
}
1505-
}
1533+
let run_setup =
1534+
self.ensure_addresses_cached(max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE))?;
15061535

15071536
debug!("run_setup: {}", run_setup);
15081537
// TODO: what if i generate an address first and cache some addresses?
@@ -3953,6 +3982,48 @@ pub(crate) mod test {
39533982
builder.add_recipient(addr.script_pubkey(), 45_000);
39543983
builder.finish().unwrap();
39553984
}
3985+
3986+
#[test]
3987+
fn test_get_address() {
3988+
use crate::descriptor::template::Bip84;
3989+
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
3990+
let wallet = Wallet::new(
3991+
Bip84(key, KeychainKind::External),
3992+
Some(Bip84(key, KeychainKind::Internal)),
3993+
Network::Regtest,
3994+
MemoryDatabase::default(),
3995+
)
3996+
.unwrap();
3997+
3998+
assert_eq!(
3999+
wallet.get_address(AddressIndex::New).unwrap().address,
4000+
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap()
4001+
);
4002+
assert_eq!(
4003+
wallet
4004+
.get_internal_address(AddressIndex::New)
4005+
.unwrap()
4006+
.address,
4007+
Address::from_str("bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa").unwrap()
4008+
);
4009+
4010+
let wallet = Wallet::new(
4011+
Bip84(key, KeychainKind::External),
4012+
None,
4013+
Network::Regtest,
4014+
MemoryDatabase::default(),
4015+
)
4016+
.unwrap();
4017+
4018+
assert_eq!(
4019+
wallet
4020+
.get_internal_address(AddressIndex::New)
4021+
.unwrap()
4022+
.address,
4023+
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap(),
4024+
"when there's no internal descriptor it should just use external"
4025+
);
4026+
}
39564027
}
39574028

39584029
/// Deterministically generate a unique name given the descriptors defining the wallet

0 commit comments

Comments
 (0)