chore(chain): Standardise KeychainTxOutIndex return types

The previous commit b9c5b9d08b040faf6c6b2d9b3745918031555b72 added
IndexSpk. This goes further and adds `Indexed` and `KeychainIndexed`
type alises (IndexSpk is Indexed<ScriptBuf>) and attempts to standardize
the structure of return types more generally.
This commit is contained in:
LLFourn 2024-06-07 12:29:58 +10:00 committed by 志宇
parent bce070b1d6
commit 101a09a97f
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
12 changed files with 103 additions and 90 deletions

View File

@ -12,7 +12,7 @@
#[cfg(feature = "miniscript")] #[cfg(feature = "miniscript")]
mod txout_index; mod txout_index;
use bitcoin::Amount; use bitcoin::{Amount, ScriptBuf};
#[cfg(feature = "miniscript")] #[cfg(feature = "miniscript")]
pub use txout_index::*; pub use txout_index::*;
@ -49,6 +49,11 @@ impl Balance {
} }
} }
/// A tuple of keychain index and corresponding [`ScriptBuf`].
pub type Indexed<T> = (u32, T);
/// A tuple of keychain, index and the [`ScriptBuf`] derived at that location.
pub type KeychainIndexed<K, T> = ((K, u32), T);
impl core::fmt::Display for Balance { impl core::fmt::Display for Balance {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!( write!(

View File

@ -3,7 +3,7 @@ use crate::{
indexed_tx_graph::Indexer, indexed_tx_graph::Indexer,
miniscript::{Descriptor, DescriptorPublicKey}, miniscript::{Descriptor, DescriptorPublicKey},
spk_iter::BIP32_MAX_INDEX, spk_iter::BIP32_MAX_INDEX,
DescriptorExt, DescriptorId, IndexSpk, SpkIterator, SpkTxOutIndex, DescriptorExt, DescriptorId, SpkIterator, SpkTxOutIndex,
}; };
use alloc::{borrow::ToOwned, vec::Vec}; use alloc::{borrow::ToOwned, vec::Vec};
use bitcoin::{Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid}; use bitcoin::{Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid};
@ -12,6 +12,7 @@ use core::{
ops::{Bound, RangeBounds}, ops::{Bound, RangeBounds},
}; };
use super::*;
use crate::Append; use crate::Append;
/// Represents updates to the derivation index of a [`KeychainTxOutIndex`]. /// Represents updates to the derivation index of a [`KeychainTxOutIndex`].
@ -140,7 +141,7 @@ pub const DEFAULT_LOOKAHEAD: u32 = 25;
/// # Change sets /// # Change sets
/// ///
/// Methods that can update the last revealed index or add keychains will return [`ChangeSet`] to report /// Methods that can update the last revealed index or add keychains will return [`ChangeSet`] to report
/// these changes. This can be persisted for future recovery. /// these changes. This should be persisted for future recovery.
/// ///
/// ## Synopsis /// ## Synopsis
/// ///
@ -191,18 +192,18 @@ pub const DEFAULT_LOOKAHEAD: u32 = 25;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct KeychainTxOutIndex<K> { pub struct KeychainTxOutIndex<K> {
inner: SpkTxOutIndex<(K, u32)>, inner: SpkTxOutIndex<(K, u32)>,
// keychain -> (descriptor, descriptor id) map /// keychain -> (descriptor id) map
keychains_to_descriptor_ids: BTreeMap<K, DescriptorId>, keychains_to_descriptor_ids: BTreeMap<K, DescriptorId>,
// descriptor id -> keychain map /// descriptor id -> keychain map
descriptor_ids_to_keychains: BTreeMap<DescriptorId, K>, descriptor_ids_to_keychains: BTreeMap<DescriptorId, K>,
// descriptor_id -> descriptor map /// descriptor_id -> descriptor map
// This is a "monotone" map, meaning that its size keeps growing, i.e., we never delete /// This is a "monotone" map, meaning that its size keeps growing, i.e., we never delete
// descriptors from it. This is useful for revealing spks for descriptors that don't have /// descriptors from it. This is useful for revealing spks for descriptors that don't have
// keychains associated. /// keychains associated.
descriptor_ids_to_descriptors: BTreeMap<DescriptorId, Descriptor<DescriptorPublicKey>>, descriptor_ids_to_descriptors: BTreeMap<DescriptorId, Descriptor<DescriptorPublicKey>>,
// last revealed indexes /// last revealed indices for each descriptor.
last_revealed: HashMap<DescriptorId, u32>, last_revealed: HashMap<DescriptorId, u32>,
// lookahead settings for each keychain /// lookahead setting
lookahead: u32, lookahead: u32,
} }
@ -292,21 +293,28 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
} }
/// Get the set of indexed outpoints, corresponding to tracked keychains. /// Get the set of indexed outpoints, corresponding to tracked keychains.
pub fn outpoints(&self) -> &BTreeSet<((K, u32), OutPoint)> { pub fn outpoints(&self) -> &BTreeSet<KeychainIndexed<K, OutPoint>> {
self.inner.outpoints() self.inner.outpoints()
} }
/// Iterate over known txouts that spend to tracked script pubkeys. /// Iterate over known txouts that spend to tracked script pubkeys.
pub fn txouts(&self) -> impl DoubleEndedIterator<Item = (&(K, u32), OutPoint, &TxOut)> + '_ { pub fn txouts(
self.inner.txouts() &self,
) -> impl DoubleEndedIterator<Item = KeychainIndexed<K, (OutPoint, &TxOut)>> + ExactSizeIterator
{
self.inner
.txouts()
.map(|(index, op, txout)| (index.clone(), (op, txout)))
} }
/// Finds all txouts on a transaction that has previously been scanned and indexed. /// Finds all txouts on a transaction that has previously been scanned and indexed.
pub fn txouts_in_tx( pub fn txouts_in_tx(
&self, &self,
txid: Txid, txid: Txid,
) -> impl DoubleEndedIterator<Item = (&(K, u32), OutPoint, &TxOut)> { ) -> impl DoubleEndedIterator<Item = KeychainIndexed<K, (OutPoint, &TxOut)>> {
self.inner.txouts_in_tx(txid) self.inner
.txouts_in_tx(txid)
.map(|(index, op, txout)| (index.clone(), (op, txout)))
} }
/// Return the [`TxOut`] of `outpoint` if it has been indexed, and if it corresponds to a /// Return the [`TxOut`] of `outpoint` if it has been indexed, and if it corresponds to a
@ -315,8 +323,10 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// The associated keychain and keychain index of the txout's spk is also returned. /// The associated keychain and keychain index of the txout's spk is also returned.
/// ///
/// This calls [`SpkTxOutIndex::txout`] internally. /// This calls [`SpkTxOutIndex::txout`] internally.
pub fn txout(&self, outpoint: OutPoint) -> Option<(&(K, u32), &TxOut)> { pub fn txout(&self, outpoint: OutPoint) -> Option<KeychainIndexed<K, &TxOut>> {
self.inner.txout(outpoint) self.inner
.txout(outpoint)
.map(|(index, txout)| (index.clone(), txout))
} }
/// Return the script that exists under the given `keychain`'s `index`. /// Return the script that exists under the given `keychain`'s `index`.
@ -420,12 +430,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// Insert a descriptor with a keychain associated to it. /// Insert a descriptor with a keychain associated to it.
/// ///
/// Adding a descriptor means you will be able to derive new script pubkeys under it /// Adding a descriptor means you will be able to derive new script pubkeys under it and the
/// and the txout index will discover transaction outputs with those script pubkeys. /// txout index will discover transaction outputs with those script pubkeys (once they've been
/// derived and added to the index).
/// ///
/// keychain <-> descriptor is a one-to-one mapping -- in `--release` this method will ignore calls that try to /// keychain <-> descriptor is a one-to-one mapping that cannot be changed. Attempting to do so
/// associate a keychain with the descriptor of another keychain or to re-assign the keychain to /// will return a [`InsertDescriptorError<K>`].
/// new descriptor. If `debug_assertions` are enabled then it will panic.
pub fn insert_descriptor( pub fn insert_descriptor(
&mut self, &mut self,
keychain: K, keychain: K,
@ -575,7 +585,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
pub fn revealed_spks( pub fn revealed_spks(
&self, &self,
range: impl RangeBounds<K>, range: impl RangeBounds<K>,
) -> impl Iterator<Item = (&(K, u32), &Script)> { ) -> impl Iterator<Item = KeychainIndexed<K, &Script>> {
let start = range.start_bound(); let start = range.start_bound();
let end = range.end_bound(); let end = range.end_bound();
let mut iter_last_revealed = self let mut iter_last_revealed = self
@ -593,8 +603,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
// iterate through the last_revealed for each keychain and the spks for each keychain in // iterate through the last_revealed for each keychain and the spks for each keychain in
// tandem. This minimizes BTreeMap queries. // tandem. This minimizes BTreeMap queries.
core::iter::from_fn(move || loop { core::iter::from_fn(move || loop {
let (path, spk) = iter_spks.next()?; let ((keychain, index), spk) = iter_spks.next()?;
let (keychain, index) = path;
// We need to find the last revealed that matches the current spk we are considering so // We need to find the last revealed that matches the current spk we are considering so
// we skip ahead. // we skip ahead.
while current_keychain?.0 < keychain { while current_keychain?.0 < keychain {
@ -603,7 +612,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
let (current_keychain, last_revealed) = current_keychain?; let (current_keychain, last_revealed) = current_keychain?;
if current_keychain == keychain && Some(*index) <= last_revealed { if current_keychain == keychain && Some(*index) <= last_revealed {
break Some((path, spk.as_script())); break Some(((keychain.clone(), *index), spk.as_script()));
} }
}) })
} }
@ -615,7 +624,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
pub fn revealed_keychain_spks<'a>( pub fn revealed_keychain_spks<'a>(
&'a self, &'a self,
keychain: &'a K, keychain: &'a K,
) -> impl DoubleEndedIterator<Item = (u32, &Script)> + 'a { ) -> impl DoubleEndedIterator<Item = Indexed<&Script>> + 'a {
let end = self let end = self
.last_revealed_index(keychain) .last_revealed_index(keychain)
.map(|v| v + 1) .map(|v| v + 1)
@ -627,7 +636,9 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
} }
/// Iterate over revealed, but unused, spks of all keychains. /// Iterate over revealed, but unused, spks of all keychains.
pub fn unused_spks(&self) -> impl DoubleEndedIterator<Item = ((K, u32), &Script)> + Clone { pub fn unused_spks(
&self,
) -> impl DoubleEndedIterator<Item = KeychainIndexed<K, &Script>> + Clone {
self.keychains_to_descriptor_ids self.keychains_to_descriptor_ids
.keys() .keys()
.flat_map(|keychain| { .flat_map(|keychain| {
@ -641,7 +652,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
pub fn unused_keychain_spks( pub fn unused_keychain_spks(
&self, &self,
keychain: &K, keychain: &K,
) -> impl DoubleEndedIterator<Item = (u32, &Script)> + Clone { ) -> impl DoubleEndedIterator<Item = Indexed<&Script>> + Clone {
let end = match self.keychains_to_descriptor_ids.get(keychain) { let end = match self.keychains_to_descriptor_ids.get(keychain) {
Some(did) => self.last_revealed.get(did).map(|v| *v + 1).unwrap_or(0), Some(did) => self.last_revealed.get(did).map(|v| *v + 1).unwrap_or(0),
None => 0, None => 0,
@ -739,16 +750,16 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
&mut self, &mut self,
keychain: &K, keychain: &K,
target_index: u32, target_index: u32,
) -> Option<(Vec<IndexSpk>, ChangeSet<K>)> { ) -> Option<(Vec<Indexed<ScriptBuf>>, ChangeSet<K>)> {
let mut changeset = ChangeSet::default(); let mut changeset = ChangeSet::default();
let mut spks: Vec<IndexSpk> = vec![]; let mut spks: Vec<Indexed<ScriptBuf>> = vec![];
while let Some((i, new)) = self.next_index(keychain) { while let Some((i, new)) = self.next_index(keychain) {
if !new || i > target_index { if !new || i > target_index {
break; break;
} }
match self.reveal_next_spk(keychain) { match self.reveal_next_spk(keychain) {
Some(((i, spk), change)) => { Some(((i, spk), change)) => {
spks.push((i, spk.to_owned())); spks.push((i, spk));
changeset.append(change); changeset.append(change);
} }
None => break, None => break,
@ -770,7 +781,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// 1. The descriptor has no wildcard and already has one script revealed. /// 1. The descriptor has no wildcard and already has one script revealed.
/// 2. The descriptor has already revealed scripts up to the numeric bound. /// 2. The descriptor has already revealed scripts up to the numeric bound.
/// 3. There is no descriptor associated with the given keychain. /// 3. There is no descriptor associated with the given keychain.
pub fn reveal_next_spk(&mut self, keychain: &K) -> Option<((u32, &Script), ChangeSet<K>)> { pub fn reveal_next_spk(&mut self, keychain: &K) -> Option<(Indexed<ScriptBuf>, ChangeSet<K>)> {
let (next_index, new) = self.next_index(keychain)?; let (next_index, new) = self.next_index(keychain)?;
let mut changeset = ChangeSet::default(); let mut changeset = ChangeSet::default();
@ -790,7 +801,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
.inner .inner
.spk_at_index(&(keychain.clone(), next_index)) .spk_at_index(&(keychain.clone(), next_index))
.expect("we just inserted it"); .expect("we just inserted it");
Some(((next_index, script), changeset)) Some(((next_index, script.into()), changeset))
} }
/// Gets the next unused script pubkey in the keychain. I.e., the script pubkey with the lowest /// Gets the next unused script pubkey in the keychain. I.e., the script pubkey with the lowest
@ -802,20 +813,17 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// has used all scripts up to the derivation bounds, then the last derived script pubkey will be /// has used all scripts up to the derivation bounds, then the last derived script pubkey will be
/// returned. /// returned.
/// ///
/// Returns None if the provided keychain doesn't exist. /// Returns `None` if there are no script pubkeys that have been used and no new script pubkey
pub fn next_unused_spk(&mut self, keychain: &K) -> Option<((u32, &Script), ChangeSet<K>)> { /// could be revealed (see [`reveal_next_spk`] for when this happens).
let need_new = self.unused_keychain_spks(keychain).next().is_none(); ///
// this rather strange branch is needed because of some lifetime issues /// [`reveal_next_spk`]: Self::reveal_next_spk
if need_new { pub fn next_unused_spk(&mut self, keychain: &K) -> Option<(Indexed<ScriptBuf>, ChangeSet<K>)> {
self.reveal_next_spk(keychain) let next_unused = self
} else { .unused_keychain_spks(keychain)
Some(( .next()
self.unused_keychain_spks(keychain) .map(|(i, spk)| ((i, spk.to_owned()), ChangeSet::default()));
.next()
.expect("we already know next exists"), next_unused.or_else(|| self.reveal_next_spk(keychain))
ChangeSet::default(),
))
}
} }
/// Iterate over all [`OutPoint`]s that have `TxOut`s with script pubkeys derived from /// Iterate over all [`OutPoint`]s that have `TxOut`s with script pubkeys derived from
@ -823,17 +831,19 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
pub fn keychain_outpoints<'a>( pub fn keychain_outpoints<'a>(
&'a self, &'a self,
keychain: &'a K, keychain: &'a K,
) -> impl DoubleEndedIterator<Item = (u32, OutPoint)> + 'a { ) -> impl DoubleEndedIterator<Item = Indexed<OutPoint>> + 'a {
self.keychain_outpoints_in_range(keychain..=keychain) self.keychain_outpoints_in_range(keychain..=keychain)
.map(|((_, i), op)| (*i, op)) .map(|((_, i), op)| (i, op))
} }
/// Iterate over [`OutPoint`]s that have script pubkeys derived from keychains in `range`. /// Iterate over [`OutPoint`]s that have script pubkeys derived from keychains in `range`.
pub fn keychain_outpoints_in_range<'a>( pub fn keychain_outpoints_in_range<'a>(
&'a self, &'a self,
range: impl RangeBounds<K> + 'a, range: impl RangeBounds<K> + 'a,
) -> impl DoubleEndedIterator<Item = (&(K, u32), OutPoint)> + 'a { ) -> impl DoubleEndedIterator<Item = KeychainIndexed<K, OutPoint>> + 'a {
self.inner.outputs_in_range(self.map_to_inner_bounds(range)) self.inner
.outputs_in_range(self.map_to_inner_bounds(range))
.map(|((k, i), op)| ((k.clone(), *i), op))
} }
fn map_to_inner_bounds(&self, bound: impl RangeBounds<K>) -> impl RangeBounds<(K, u32)> { fn map_to_inner_bounds(&self, bound: impl RangeBounds<K>) -> impl RangeBounds<(K, u32)> {

View File

@ -28,6 +28,7 @@ pub use chain_data::*;
pub mod indexed_tx_graph; pub mod indexed_tx_graph;
pub use indexed_tx_graph::IndexedTxGraph; pub use indexed_tx_graph::IndexedTxGraph;
pub mod keychain; pub mod keychain;
pub use keychain::{Indexed, KeychainIndexed};
pub mod local_chain; pub mod local_chain;
mod tx_data_traits; mod tx_data_traits;
pub mod tx_graph; pub mod tx_graph;

View File

@ -1,7 +1,8 @@
//! Helper types for spk-based blockchain clients. //! Helper types for spk-based blockchain clients.
use crate::{ use crate::{
collections::BTreeMap, local_chain::CheckPoint, ConfirmationTimeHeightAnchor, IndexSpk, TxGraph, collections::BTreeMap, keychain::Indexed, local_chain::CheckPoint,
ConfirmationTimeHeightAnchor, TxGraph,
}; };
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use bitcoin::{OutPoint, Script, ScriptBuf, Txid}; use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
@ -195,7 +196,7 @@ pub struct FullScanRequest<K> {
/// [`LocalChain::tip`]: crate::local_chain::LocalChain::tip /// [`LocalChain::tip`]: crate::local_chain::LocalChain::tip
pub chain_tip: CheckPoint, pub chain_tip: CheckPoint,
/// Iterators of script pubkeys indexed by the keychain index. /// Iterators of script pubkeys indexed by the keychain index.
pub spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = IndexSpk> + Send>>, pub spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
} }
impl<K: Ord + Clone> FullScanRequest<K> { impl<K: Ord + Clone> FullScanRequest<K> {
@ -238,7 +239,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
pub fn set_spks_for_keychain( pub fn set_spks_for_keychain(
mut self, mut self,
keychain: K, keychain: K,
spks: impl IntoIterator<IntoIter = impl Iterator<Item = IndexSpk> + Send + 'static>, spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
) -> Self { ) -> Self {
self.spks_by_keychain self.spks_by_keychain
.insert(keychain, Box::new(spks.into_iter())); .insert(keychain, Box::new(spks.into_iter()));
@ -252,7 +253,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
pub fn chain_spks_for_keychain( pub fn chain_spks_for_keychain(
mut self, mut self,
keychain: K, keychain: K,
spks: impl IntoIterator<IntoIter = impl Iterator<Item = IndexSpk> + Send + 'static>, spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
) -> Self { ) -> Self {
match self.spks_by_keychain.remove(&keychain) { match self.spks_by_keychain.remove(&keychain) {
// clippy here suggests to remove `into_iter` from `spks.into_iter()`, but doing so // clippy here suggests to remove `into_iter` from `spks.into_iter()`, but doing so

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
bitcoin::{secp256k1::Secp256k1, ScriptBuf}, bitcoin::{secp256k1::Secp256k1, ScriptBuf},
keychain::Indexed,
miniscript::{Descriptor, DescriptorPublicKey}, miniscript::{Descriptor, DescriptorPublicKey},
}; };
use core::{borrow::Borrow, ops::Bound, ops::RangeBounds}; use core::{borrow::Borrow, ops::Bound, ops::RangeBounds};
@ -7,9 +8,6 @@ use core::{borrow::Borrow, ops::Bound, ops::RangeBounds};
/// Maximum [BIP32](https://bips.xyz/32) derivation index. /// Maximum [BIP32](https://bips.xyz/32) derivation index.
pub const BIP32_MAX_INDEX: u32 = (1 << 31) - 1; pub const BIP32_MAX_INDEX: u32 = (1 << 31) - 1;
/// A tuple of keychain index and corresponding [`ScriptBuf`].
pub type IndexSpk = (u32, ScriptBuf);
/// An iterator for derived script pubkeys. /// An iterator for derived script pubkeys.
/// ///
/// [`SpkIterator`] is an implementation of the [`Iterator`] trait which possesses its own `next()` /// [`SpkIterator`] is an implementation of the [`Iterator`] trait which possesses its own `next()`
@ -100,7 +98,7 @@ impl<D> Iterator for SpkIterator<D>
where where
D: Borrow<Descriptor<DescriptorPublicKey>>, D: Borrow<Descriptor<DescriptorPublicKey>>,
{ {
type Item = IndexSpk; type Item = Indexed<ScriptBuf>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after. // For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after.

View File

@ -408,10 +408,10 @@ fn test_wildcard_derivations() {
// - next_unused() == ((0, <spk>), keychain::ChangeSet:is_empty()) // - next_unused() == ((0, <spk>), keychain::ChangeSet:is_empty())
assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (0, true)); assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (0, true));
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap(); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap();
assert_eq!(spk, (0_u32, external_spk_0.as_script())); assert_eq!(spk, (0_u32, external_spk_0.clone()));
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 0)].into()); assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 0)].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap(); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
assert_eq!(spk, (0_u32, external_spk_0.as_script())); assert_eq!(spk, (0_u32, external_spk_0.clone()));
assert_eq!(&changeset.last_revealed, &[].into()); assert_eq!(&changeset.last_revealed, &[].into());
// - derived till 25 // - derived till 25
@ -431,12 +431,12 @@ fn test_wildcard_derivations() {
assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (26, true)); assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (26, true));
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap(); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap();
assert_eq!(spk, (26, external_spk_26.as_script())); assert_eq!(spk, (26, external_spk_26));
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 26)].into()); assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 26)].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap(); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
assert_eq!(spk, (16, external_spk_16.as_script())); assert_eq!(spk, (16, external_spk_16));
assert_eq!(&changeset.last_revealed, &[].into()); assert_eq!(&changeset.last_revealed, &[].into());
// - Use all the derived till 26. // - Use all the derived till 26.
@ -446,7 +446,7 @@ fn test_wildcard_derivations() {
}); });
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap(); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
assert_eq!(spk, (27, external_spk_27.as_script())); assert_eq!(spk, (27, external_spk_27));
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 27)].into()); assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 27)].into());
} }
@ -479,7 +479,7 @@ fn test_non_wildcard_derivations() {
let (spk, changeset) = txout_index let (spk, changeset) = txout_index
.reveal_next_spk(&TestKeychain::External) .reveal_next_spk(&TestKeychain::External)
.unwrap(); .unwrap();
assert_eq!(spk, (0, external_spk.as_script())); assert_eq!(spk, (0, external_spk.clone()));
assert_eq!( assert_eq!(
&changeset.last_revealed, &changeset.last_revealed,
&[(no_wildcard_descriptor.descriptor_id(), 0)].into() &[(no_wildcard_descriptor.descriptor_id(), 0)].into()
@ -488,7 +488,7 @@ fn test_non_wildcard_derivations() {
let (spk, changeset) = txout_index let (spk, changeset) = txout_index
.next_unused_spk(&TestKeychain::External) .next_unused_spk(&TestKeychain::External)
.unwrap(); .unwrap();
assert_eq!(spk, (0, external_spk.as_script())); assert_eq!(spk, (0, external_spk.clone()));
assert_eq!(&changeset.last_revealed, &[].into()); assert_eq!(&changeset.last_revealed, &[].into());
// given: // given:
@ -506,13 +506,13 @@ fn test_non_wildcard_derivations() {
let (spk, changeset) = txout_index let (spk, changeset) = txout_index
.reveal_next_spk(&TestKeychain::External) .reveal_next_spk(&TestKeychain::External)
.unwrap(); .unwrap();
assert_eq!(spk, (0, external_spk.as_script())); assert_eq!(spk, (0, external_spk.clone()));
assert_eq!(&changeset.last_revealed, &[].into()); assert_eq!(&changeset.last_revealed, &[].into());
let (spk, changeset) = txout_index let (spk, changeset) = txout_index
.next_unused_spk(&TestKeychain::External) .next_unused_spk(&TestKeychain::External)
.unwrap(); .unwrap();
assert_eq!(spk, (0, external_spk.as_script())); assert_eq!(spk, (0, external_spk.clone()));
assert_eq!(&changeset.last_revealed, &[].into()); assert_eq!(&changeset.last_revealed, &[].into());
let (revealed_spks, revealed_changeset) = txout_index let (revealed_spks, revealed_changeset) = txout_index
.reveal_to_target(&TestKeychain::External, 200) .reveal_to_target(&TestKeychain::External, 200)

View File

@ -8,7 +8,7 @@ use bdk_chain::{
local_chain::CheckPoint, local_chain::CheckPoint,
BlockId, ConfirmationTimeHeightAnchor, TxGraph, BlockId, ConfirmationTimeHeightAnchor, TxGraph,
}; };
use bdk_chain::{Anchor, IndexSpk}; use bdk_chain::{Anchor, Indexed};
use esplora_client::{Amount, TxStatus}; use esplora_client::{Amount, TxStatus};
use futures::{stream::FuturesOrdered, TryStreamExt}; use futures::{stream::FuturesOrdered, TryStreamExt};
@ -236,7 +236,7 @@ async fn full_scan_for_index_and_graph<K: Ord + Clone + Send>(
client: &esplora_client::AsyncClient, client: &esplora_client::AsyncClient,
keychain_spks: BTreeMap< keychain_spks: BTreeMap<
K, K,
impl IntoIterator<IntoIter = impl Iterator<Item = IndexSpk> + Send> + Send, impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send> + Send,
>, >,
stop_gap: usize, stop_gap: usize,
parallel_requests: usize, parallel_requests: usize,

View File

@ -9,7 +9,7 @@ use bdk_chain::{
local_chain::CheckPoint, local_chain::CheckPoint,
BlockId, ConfirmationTimeHeightAnchor, TxGraph, BlockId, ConfirmationTimeHeightAnchor, TxGraph,
}; };
use bdk_chain::{Anchor, IndexSpk}; use bdk_chain::{Anchor, Indexed};
use esplora_client::TxStatus; use esplora_client::TxStatus;
use crate::anchor_from_status; use crate::anchor_from_status;
@ -217,7 +217,7 @@ fn chain_update<A: Anchor>(
/// [`KeychainTxOutIndex`](bdk_chain::keychain::KeychainTxOutIndex). /// [`KeychainTxOutIndex`](bdk_chain::keychain::KeychainTxOutIndex).
fn full_scan_for_index_and_graph_blocking<K: Ord + Clone>( fn full_scan_for_index_and_graph_blocking<K: Ord + Clone>(
client: &esplora_client::BlockingClient, client: &esplora_client::BlockingClient,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = IndexSpk>>, keychain_spks: BTreeMap<K, impl IntoIterator<Item = Indexed<ScriptBuf>>>,
stop_gap: usize, stop_gap: usize,
parallel_requests: usize, parallel_requests: usize,
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error> { ) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error> {

View File

@ -29,7 +29,7 @@ use bdk_chain::{
spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult}, spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult},
tx_graph::{CanonicalTx, TxGraph}, tx_graph::{CanonicalTx, TxGraph},
Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut, Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut,
IndexSpk, IndexedTxGraph, Indexed, IndexedTxGraph,
}; };
use bdk_persist::{Persist, PersistBackend}; use bdk_persist::{Persist, PersistBackend};
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
@ -752,7 +752,8 @@ impl Wallet {
Ok(AddressInfo { Ok(AddressInfo {
index, index,
address: Address::from_script(spk, self.network).expect("must have address form"), address: Address::from_script(spk.as_script(), self.network)
.expect("must have address form"),
keychain, keychain,
}) })
} }
@ -809,7 +810,8 @@ impl Wallet {
Ok(AddressInfo { Ok(AddressInfo {
index, index,
address: Address::from_script(spk, self.network).expect("must have address form"), address: Address::from_script(spk.as_script(), self.network)
.expect("must have address form"),
keychain, keychain,
}) })
} }
@ -910,7 +912,7 @@ impl Wallet {
/// script pubkeys the wallet is storing internally). /// script pubkeys the wallet is storing internally).
pub fn all_unbounded_spk_iters( pub fn all_unbounded_spk_iters(
&self, &self,
) -> BTreeMap<KeychainKind, impl Iterator<Item = IndexSpk> + Clone> { ) -> BTreeMap<KeychainKind, impl Iterator<Item = Indexed<ScriptBuf>> + Clone> {
self.indexed_graph.index.all_unbounded_spk_iters() self.indexed_graph.index.all_unbounded_spk_iters()
} }
@ -922,7 +924,7 @@ impl Wallet {
pub fn unbounded_spk_iter( pub fn unbounded_spk_iter(
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
) -> impl Iterator<Item = IndexSpk> + Clone { ) -> impl Iterator<Item = Indexed<ScriptBuf>> + Clone {
self.indexed_graph self.indexed_graph
.index .index
.unbounded_spk_iter(&keychain) .unbounded_spk_iter(&keychain)
@ -932,7 +934,7 @@ impl Wallet {
/// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
/// wallet's database. /// wallet's database.
pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> { pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
let (&(keychain, index), _) = self.indexed_graph.index.txout(op)?; let ((keychain, index), _) = self.indexed_graph.index.txout(op)?;
self.indexed_graph self.indexed_graph
.graph() .graph()
.filter_chain_unspents( .filter_chain_unspents(
@ -1511,7 +1513,6 @@ impl Wallet {
.index .index
.next_unused_spk(&change_keychain) .next_unused_spk(&change_keychain)
.expect("keychain must exist"); .expect("keychain must exist");
let spk = spk.into();
self.indexed_graph.index.mark_used(change_keychain, index); self.indexed_graph.index.mark_used(change_keychain, index);
self.persist self.persist
.stage(ChangeSet::from(indexed_tx_graph::ChangeSet::from( .stage(ChangeSet::from(indexed_tx_graph::ChangeSet::from(

View File

@ -265,9 +265,6 @@ where
.expect("Must exist"); .expect("Must exist");
changeset.append(change_changeset); changeset.append(change_changeset);
// Clone to drop the immutable reference.
let change_script = change_script.into();
let change_plan = bdk_tmp_plan::plan_satisfaction( let change_plan = bdk_tmp_plan::plan_satisfaction(
&graph &graph
.index .index
@ -481,8 +478,8 @@ where
local_chain::ChangeSet::default(), local_chain::ChangeSet::default(),
indexed_tx_graph::ChangeSet::from(index_changeset), indexed_tx_graph::ChangeSet::from(index_changeset),
)))?; )))?;
let addr = let addr = Address::from_script(spk.as_script(), network)
Address::from_script(spk, network).context("failed to derive address")?; .context("failed to derive address")?;
println!("[address @ {}] {}", spk_i, addr); println!("[address @ {}] {}", spk_i, addr);
Ok(()) Ok(())
} }

View File

@ -228,7 +228,7 @@ fn main() -> anyhow::Result<()> {
let all_spks = graph let all_spks = graph
.index .index
.revealed_spks(..) .revealed_spks(..)
.map(|(index, spk)| (index.to_owned(), spk.to_owned())) .map(|(index, spk)| (index, spk.to_owned()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
request = request.chain_spks(all_spks.into_iter().map(|((k, spk_i), spk)| { request = request.chain_spks(all_spks.into_iter().map(|((k, spk_i), spk)| {
eprint!("Scanning {}: {}", k, spk_i); eprint!("Scanning {}: {}", k, spk_i);
@ -239,7 +239,7 @@ fn main() -> anyhow::Result<()> {
let unused_spks = graph let unused_spks = graph
.index .index
.unused_spks() .unused_spks()
.map(|(index, spk)| (index.to_owned(), spk.to_owned())) .map(|(index, spk)| (index, spk.to_owned()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
request = request =
request.chain_spks(unused_spks.into_iter().map(move |((k, spk_i), spk)| { request.chain_spks(unused_spks.into_iter().map(move |((k, spk_i), spk)| {

View File

@ -245,7 +245,7 @@ fn main() -> anyhow::Result<()> {
let all_spks = graph let all_spks = graph
.index .index
.revealed_spks(..) .revealed_spks(..)
.map(|((k, i), spk)| (k.to_owned(), *i, spk.to_owned())) .map(|((k, i), spk)| (k, i, spk.to_owned()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
request = request.chain_spks(all_spks.into_iter().map(|(k, i, spk)| { request = request.chain_spks(all_spks.into_iter().map(|(k, i, spk)| {
eprint!("scanning {}:{}", k, i); eprint!("scanning {}:{}", k, i);
@ -258,7 +258,7 @@ fn main() -> anyhow::Result<()> {
let unused_spks = graph let unused_spks = graph
.index .index
.unused_spks() .unused_spks()
.map(|(index, spk)| (index.to_owned(), spk.to_owned())) .map(|(index, spk)| (index, spk.to_owned()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
request = request =
request.chain_spks(unused_spks.into_iter().map(move |((k, i), spk)| { request.chain_spks(unused_spks.into_iter().map(move |((k, i), spk)| {