Skip to content

Commit 0adc52a

Browse files
committed
PR feedback
* get_batch_unused_addresses loops through address indexes `from_front = true` (for firstUnused) or `false` (for lastUnused). * Relies on database having up to date script_pubkeys in such a manner that script_pks.len() == self.fetch_index(keychain) * 1 script pubkey per address index? * Must work with current_address_index = 0 Signed-off-by: nickfarrow <[email protected]>
1 parent 0a8e368 commit 0adc52a

File tree

1 file changed

+60
-107
lines changed

1 file changed

+60
-107
lines changed

src/wallet/mod.rs

+60-107
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ use bitcoin::util::psbt::Input;
3030
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
3131
use bitcoin::{Address, Network, OutPoint, Script, SigHashType, Transaction, TxOut, Txid};
3232

33-
use miniscript::descriptor::Descriptor;
3433
use miniscript::descriptor::DescriptorTrait;
3534
use miniscript::psbt::PsbtInputSatisfier;
3635

@@ -62,9 +61,9 @@ use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
6261
use crate::descriptor::derived::AsDerived;
6362
use crate::descriptor::policy::BuildSatisfaction;
6463
use crate::descriptor::{
65-
get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DerivedDescriptorKey,
66-
DerivedDescriptorMeta, DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy,
67-
IntoWalletDescriptor, Policy, XKeyUtils,
64+
get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DerivedDescriptorMeta,
65+
DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
66+
Policy, XKeyUtils,
6867
};
6968
use crate::error::Error;
7069
use crate::psbt::PsbtUtils;
@@ -261,95 +260,26 @@ where
261260
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
262261
}
263262

264-
// Return vector of unused address indexes
265-
fn get_unused_key_indexes(&self, keychain: KeychainKind) -> Result<Vec<u32>, Error> {
266-
let current_index = self.fetch_index(keychain)?;
267-
268-
let derived_keys: Vec<Descriptor<DerivedDescriptorKey>> = (0..current_index + 1)
269-
.map(|i| {
270-
self.get_descriptor_for_keychain(keychain)
271-
.as_derived(i, &self.secp)
272-
})
273-
.collect();
274-
275-
let unused_key_indexes: Vec<u32> = derived_keys
276-
.iter()
277-
.cloned()
278-
.enumerate()
279-
.filter_map(|(i, key)| {
280-
if !self
281-
.list_transactions(true)
282-
.expect("getting transaction list")
283-
.iter()
284-
.flat_map(|tx_details| tx_details.transaction.as_ref())
285-
.flat_map(|tx| tx.output.iter())
286-
.any(|o| o.script_pubkey == key.script_pubkey())
287-
{
288-
Some(i as u32)
289-
} else {
290-
None
291-
}
292-
})
293-
.collect();
294-
295-
Ok(unused_key_indexes)
263+
// Return whether this address has been used in a transaction
264+
fn has_address_been_used(&self, script_pk: &Script) -> bool {
265+
let txns = self.list_transactions(true).unwrap_or_else(|_| vec![]);
266+
txns.iter()
267+
.flat_map(|tx_details| tx_details.transaction.as_ref())
268+
.flat_map(|tx| tx.output.iter())
269+
.any(|o| o.script_pubkey == *script_pk)
296270
}
297271

298272
// Return the the last previously derived address for `keychain` if it has not been used in a
299273
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
300274
fn get_last_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
301-
let mut unused_key_indexes = self.get_unused_key_indexes(keychain)?;
302-
match unused_key_indexes.pop() {
303-
None => self.get_new_address(keychain),
304-
Some(address_index) => {
305-
let derived_key = self
306-
.get_descriptor_for_keychain(keychain)
307-
.as_derived(address_index, &self.secp);
308-
309-
let script_pubkey = derived_key.script_pubkey();
310-
311-
let found_used = self
312-
.list_transactions(true)?
313-
.iter()
314-
.flat_map(|tx_details| tx_details.transaction.as_ref())
315-
.flat_map(|tx| tx.output.iter())
316-
.any(|o| o.script_pubkey == script_pubkey);
317-
318-
if found_used {
319-
self.get_new_address(keychain)
320-
} else {
321-
derived_key
322-
.address(self.network)
323-
.map(|address| AddressInfo {
324-
address,
325-
index: address_index,
326-
keychain,
327-
})
328-
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
329-
}
330-
}
331-
}
275+
let mut unused_addresses = self.get_batch_unused_addresses(1, false, keychain)?;
276+
Ok(unused_addresses.remove(0))
332277
}
333278

334279
// Return the the first address in the keychain which has not been a recipient of a transaction
335280
fn get_first_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
336-
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
337-
if unused_key_indexes.is_empty() {
338-
self.get_new_address(keychain)
339-
} else {
340-
let derived_key = self
341-
.get_descriptor_for_keychain(keychain)
342-
.as_derived(unused_key_indexes[0], &self.secp);
343-
344-
derived_key
345-
.address(self.network)
346-
.map(|address| AddressInfo {
347-
address,
348-
index: unused_key_indexes[0],
349-
keychain,
350-
})
351-
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
352-
}
281+
let mut unused_addresses = self.get_batch_unused_addresses(1, true, keychain)?;
282+
Ok(unused_addresses.remove(0))
353283
}
354284

355285
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
@@ -463,31 +393,54 @@ where
463393
pub fn get_batch_unused_addresses(
464394
&self,
465395
n: usize,
396+
from_front: bool,
466397
keychain: KeychainKind,
467398
) -> Result<Vec<AddressInfo>, Error> {
468-
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
469-
let mut addresses = unused_key_indexes
470-
.iter()
471-
.map(|i| {
472-
let derived_key = self
473-
.get_descriptor_for_keychain(keychain)
474-
.as_derived(*i, &self.secp);
475-
476-
derived_key
477-
.address(self.network)
478-
.map(|address| AddressInfo {
479-
address,
480-
index: *i,
481-
keychain,
482-
})
483-
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
484-
})
485-
.take(n)
486-
.collect::<Result<Vec<_>, _>>()?;
487-
for _ in 0..(n - addresses.len()) {
488-
addresses.push(self.get_new_address(keychain)?)
399+
let script_pubkeys = self
400+
.database
401+
.borrow()
402+
.iter_script_pubkeys(Some(keychain))
403+
.unwrap_or_else(|_| vec![]);
404+
405+
let current_address_index = self.fetch_index(keychain)? as usize;
406+
let check_indexes = if from_front {
407+
(0..=current_address_index).collect::<Vec<_>>()
408+
} else {
409+
(0..=current_address_index).rev().collect::<Vec<_>>()
410+
};
411+
412+
let mut unused_addresses = vec![];
413+
for i in check_indexes {
414+
// if we have made a pubkey at this index, check whether the address has been used.
415+
if i < script_pubkeys.len() {
416+
let script_pk = &script_pubkeys[i];
417+
if self.has_address_been_used(script_pk) {
418+
continue;
419+
}
420+
}
421+
if let Ok(unused_address) = self
422+
.get_descriptor_for_keychain(keychain)
423+
.as_derived(i as u32, &self.secp)
424+
.address(self.network)
425+
.map(|address| AddressInfo {
426+
address,
427+
index: i as u32,
428+
keychain,
429+
})
430+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
431+
{
432+
unused_addresses.push(unused_address);
433+
}
434+
435+
if unused_addresses.len() >= n {
436+
break;
437+
}
438+
}
439+
440+
for _ in 0..(n - unused_addresses.len()) {
441+
unused_addresses.push(self.get_new_address(keychain)?)
489442
}
490-
Ok(addresses)
443+
Ok(unused_addresses)
491444
}
492445

493446
/// Return whether or not a `script` is part of this wallet (either internal or external)
@@ -4029,7 +3982,7 @@ pub(crate) mod test {
40293982

40303983
assert_eq!(
40313984
wallet
4032-
.get_batch_unused_addresses(3, KeychainKind::External)
3985+
.get_batch_unused_addresses(3, true, KeychainKind::External)
40333986
.unwrap(),
40343987
vec![
40353988
AddressInfo {

0 commit comments

Comments
 (0)