|
|
|
|
@@ -3,9 +3,9 @@ use crate::{
|
|
|
|
|
indexed_tx_graph::Indexer,
|
|
|
|
|
miniscript::{Descriptor, DescriptorPublicKey},
|
|
|
|
|
spk_iter::BIP32_MAX_INDEX,
|
|
|
|
|
SpkIterator, SpkTxOutIndex,
|
|
|
|
|
DescriptorExt, DescriptorId, SpkIterator, SpkTxOutIndex,
|
|
|
|
|
};
|
|
|
|
|
use bitcoin::{Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid};
|
|
|
|
|
use bitcoin::{hashes::Hash, Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid};
|
|
|
|
|
use core::{
|
|
|
|
|
fmt::Debug,
|
|
|
|
|
ops::{Bound, RangeBounds},
|
|
|
|
|
@@ -13,9 +13,8 @@ use core::{
|
|
|
|
|
|
|
|
|
|
use crate::Append;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Represents updates to the derivation index of a [`KeychainTxOutIndex`].
|
|
|
|
|
/// It maps each keychain `K` to its last revealed index.
|
|
|
|
|
/// It maps each keychain `K` to a descriptor and its last revealed index.
|
|
|
|
|
///
|
|
|
|
|
/// It can be applied to [`KeychainTxOutIndex`] with [`apply_changeset`]. [`ChangeSet] are
|
|
|
|
|
/// monotone in that they will never decrease the revealed derivation index.
|
|
|
|
|
@@ -35,46 +34,52 @@ use crate::Append;
|
|
|
|
|
)
|
|
|
|
|
)]
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub struct ChangeSet<K>(pub BTreeMap<K, u32>);
|
|
|
|
|
|
|
|
|
|
impl<K> ChangeSet<K> {
|
|
|
|
|
/// Get the inner map of the keychain to its new derivation index.
|
|
|
|
|
pub fn as_inner(&self) -> &BTreeMap<K, u32> {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
pub struct ChangeSet<K> {
|
|
|
|
|
/// Contains the keychains that have been added and their respective descriptor
|
|
|
|
|
pub keychains_added: BTreeMap<K, Descriptor<DescriptorPublicKey>>,
|
|
|
|
|
/// Contains for each descriptor_id the last revealed index of derivation
|
|
|
|
|
pub last_revealed: BTreeMap<DescriptorId, u32>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<K: Ord> Append for ChangeSet<K> {
|
|
|
|
|
/// Append another [`ChangeSet`] into self.
|
|
|
|
|
///
|
|
|
|
|
/// For each keychain in `keychains_added` in the given [`ChangeSet`]:
|
|
|
|
|
/// If the keychain already exist with a different descriptor, we overwrite the old descriptor.
|
|
|
|
|
///
|
|
|
|
|
/// For each `last_revealed` in the given [`ChangeSet`]:
|
|
|
|
|
/// If the keychain already exists, increase the index when the other's index > self's index.
|
|
|
|
|
/// If the keychain did not exist, append the new keychain.
|
|
|
|
|
fn append(&mut self, mut other: Self) {
|
|
|
|
|
self.0.iter_mut().for_each(|(key, index)| {
|
|
|
|
|
if let Some(other_index) = other.0.remove(key) {
|
|
|
|
|
for (keychain, descriptor) in &mut self.keychains_added {
|
|
|
|
|
if let Some(other_descriptor) = other.keychains_added.remove(keychain) {
|
|
|
|
|
*descriptor = other_descriptor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (descriptor_id, index) in &mut self.last_revealed {
|
|
|
|
|
if let Some(other_index) = other.last_revealed.remove(descriptor_id) {
|
|
|
|
|
*index = other_index.max(*index);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
|
|
|
|
|
// Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
|
|
|
|
|
self.0.extend(other.0);
|
|
|
|
|
self.keychains_added.extend(other.keychains_added);
|
|
|
|
|
self.last_revealed.extend(other.last_revealed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns whether the changeset are empty.
|
|
|
|
|
fn is_empty(&self) -> bool {
|
|
|
|
|
self.0.is_empty()
|
|
|
|
|
self.last_revealed.is_empty() && self.keychains_added.is_empty()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<K> Default for ChangeSet<K> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Default::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<K> AsRef<BTreeMap<K, u32>> for ChangeSet<K> {
|
|
|
|
|
fn as_ref(&self) -> &BTreeMap<K, u32> {
|
|
|
|
|
&self.0
|
|
|
|
|
Self {
|
|
|
|
|
last_revealed: BTreeMap::default(),
|
|
|
|
|
keychains_added: BTreeMap::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -119,7 +124,7 @@ const DEFAULT_LOOKAHEAD: u32 = 25;
|
|
|
|
|
///
|
|
|
|
|
/// # Change sets
|
|
|
|
|
///
|
|
|
|
|
/// Methods that can update the last revealed index will return [`super::ChangeSet`] to report
|
|
|
|
|
/// Methods that can update the last revealed index or add keychains will return [`super::ChangeSet`] to report
|
|
|
|
|
/// these changes. This can be persisted for future recovery.
|
|
|
|
|
///
|
|
|
|
|
/// ## Synopsis
|
|
|
|
|
@@ -144,10 +149,10 @@ const DEFAULT_LOOKAHEAD: u32 = 25;
|
|
|
|
|
/// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
|
/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
|
|
|
|
/// # let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
|
|
|
|
|
/// # let (descriptor_for_user_42, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/2/*)").unwrap();
|
|
|
|
|
/// txout_index.add_keychain(MyKeychain::External, external_descriptor);
|
|
|
|
|
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor);
|
|
|
|
|
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42);
|
|
|
|
|
/// # let (descriptor_42, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/2/*)").unwrap();
|
|
|
|
|
/// let _ = txout_index.insert_descriptor(MyKeychain::External, external_descriptor);
|
|
|
|
|
/// let _ = txout_index.insert_descriptor(MyKeychain::Internal, internal_descriptor);
|
|
|
|
|
/// let _ = txout_index.insert_descriptor(MyKeychain::MyAppUser { user_id: 42 }, descriptor_42);
|
|
|
|
|
///
|
|
|
|
|
/// let new_spk_for_user = txout_index.reveal_next_spk(&MyKeychain::MyAppUser{ user_id: 42 });
|
|
|
|
|
/// ```
|
|
|
|
|
@@ -164,13 +169,24 @@ const DEFAULT_LOOKAHEAD: u32 = 25;
|
|
|
|
|
/// [`new`]: KeychainTxOutIndex::new
|
|
|
|
|
/// [`unbounded_spk_iter`]: KeychainTxOutIndex::unbounded_spk_iter
|
|
|
|
|
/// [`all_unbounded_spk_iters`]: KeychainTxOutIndex::all_unbounded_spk_iters
|
|
|
|
|
// Under the hood, the KeychainTxOutIndex uses a SpkTxOutIndex that keeps track of spks, indexed by
|
|
|
|
|
// descriptors. Users then assign or unassign keychains to those descriptors. It's important to
|
|
|
|
|
// note that descriptors, once added, never get removed from the SpkTxOutIndex; this is useful in
|
|
|
|
|
// case a user unassigns a keychain from a descriptor and after some time assigns it again.
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct KeychainTxOutIndex<K> {
|
|
|
|
|
inner: SpkTxOutIndex<(K, u32)>,
|
|
|
|
|
// descriptors of each keychain
|
|
|
|
|
keychains: BTreeMap<K, Descriptor<DescriptorPublicKey>>,
|
|
|
|
|
inner: SpkTxOutIndex<(DescriptorId, u32)>,
|
|
|
|
|
// keychain -> (descriptor, descriptor id) map
|
|
|
|
|
keychains_to_descriptors: BTreeMap<K, (DescriptorId, Descriptor<DescriptorPublicKey>)>,
|
|
|
|
|
// descriptor id -> keychain map
|
|
|
|
|
descriptor_ids_to_keychain: BTreeMap<DescriptorId, (K, Descriptor<DescriptorPublicKey>)>,
|
|
|
|
|
// descriptor_id -> descriptor map
|
|
|
|
|
// 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
|
|
|
|
|
// keychains associated.
|
|
|
|
|
descriptor_ids_to_descriptors: BTreeMap<DescriptorId, Descriptor<DescriptorPublicKey>>,
|
|
|
|
|
// last revealed indexes
|
|
|
|
|
last_revealed: BTreeMap<K, u32>,
|
|
|
|
|
last_revealed: BTreeMap<DescriptorId, u32>,
|
|
|
|
|
// lookahead settings for each keychain
|
|
|
|
|
lookahead: u32,
|
|
|
|
|
}
|
|
|
|
|
@@ -186,7 +202,15 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
|
|
|
|
|
|
|
|
|
|
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
|
|
|
|
|
match self.inner.scan_txout(outpoint, txout).cloned() {
|
|
|
|
|
Some((keychain, index)) => self.reveal_to_target(&keychain, index).1,
|
|
|
|
|
Some((descriptor_id, index)) => {
|
|
|
|
|
// We want to reveal spks for descriptors that aren't tracked by any keychain, and
|
|
|
|
|
// so we call reveal with descriptor_id
|
|
|
|
|
if let Some((_, changeset)) = self.reveal_to_target_with_id(descriptor_id, index) {
|
|
|
|
|
changeset
|
|
|
|
|
} else {
|
|
|
|
|
super::ChangeSet::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => super::ChangeSet::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -200,7 +224,13 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn initial_changeset(&self) -> Self::ChangeSet {
|
|
|
|
|
super::ChangeSet(self.last_revealed.clone())
|
|
|
|
|
super::ChangeSet {
|
|
|
|
|
keychains_added: self
|
|
|
|
|
.keychains()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.clone()))
|
|
|
|
|
.collect(),
|
|
|
|
|
last_revealed: self.last_revealed.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_changeset(&mut self, changeset: Self::ChangeSet) {
|
|
|
|
|
@@ -226,7 +256,9 @@ impl<K> KeychainTxOutIndex<K> {
|
|
|
|
|
pub fn new(lookahead: u32) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
inner: SpkTxOutIndex::default(),
|
|
|
|
|
keychains: BTreeMap::new(),
|
|
|
|
|
descriptor_ids_to_keychain: BTreeMap::new(),
|
|
|
|
|
descriptor_ids_to_descriptors: BTreeMap::new(),
|
|
|
|
|
keychains_to_descriptors: BTreeMap::new(),
|
|
|
|
|
last_revealed: BTreeMap::new(),
|
|
|
|
|
lookahead,
|
|
|
|
|
}
|
|
|
|
|
@@ -239,22 +271,29 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// **WARNING:** The internal index will contain lookahead spks. Refer to
|
|
|
|
|
/// [struct-level docs](KeychainTxOutIndex) for more about `lookahead`.
|
|
|
|
|
pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
|
|
|
|
|
pub fn inner(&self) -> &SpkTxOutIndex<(DescriptorId, u32)> {
|
|
|
|
|
&self.inner
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get a reference to the set of indexed outpoints.
|
|
|
|
|
pub fn outpoints(&self) -> &BTreeSet<((K, u32), OutPoint)> {
|
|
|
|
|
self.inner.outpoints()
|
|
|
|
|
/// Get the set of indexed outpoints, corresponding to tracked keychains.
|
|
|
|
|
pub fn outpoints(&self) -> impl DoubleEndedIterator<Item = ((K, u32), OutPoint)> + '_ {
|
|
|
|
|
self.inner
|
|
|
|
|
.outpoints()
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|((desc_id, index), op)| {
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(desc_id)
|
|
|
|
|
.map(|(k, _)| ((k.clone(), *index), *op))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iterate over known txouts that spend to tracked script pubkeys.
|
|
|
|
|
pub fn txouts(
|
|
|
|
|
&self,
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (K, u32, OutPoint, &TxOut)> + ExactSizeIterator {
|
|
|
|
|
self.inner
|
|
|
|
|
.txouts()
|
|
|
|
|
.map(|((k, i), op, txo)| (k.clone(), *i, op, txo))
|
|
|
|
|
pub fn txouts(&self) -> impl DoubleEndedIterator<Item = (K, u32, OutPoint, &TxOut)> + '_ {
|
|
|
|
|
self.inner.txouts().filter_map(|((desc_id, i), op, txo)| {
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(desc_id)
|
|
|
|
|
.map(|(k, _)| (k.clone(), *i, op, txo))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Finds all txouts on a transaction that has previously been scanned and indexed.
|
|
|
|
|
@@ -264,32 +303,41 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (K, u32, OutPoint, &TxOut)> {
|
|
|
|
|
self.inner
|
|
|
|
|
.txouts_in_tx(txid)
|
|
|
|
|
.map(|((k, i), op, txo)| (k.clone(), *i, op, txo))
|
|
|
|
|
.filter_map(|((desc_id, i), op, txo)| {
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(desc_id)
|
|
|
|
|
.map(|(k, _)| (k.clone(), *i, op, txo))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Return the [`TxOut`] of `outpoint` if it has been indexed.
|
|
|
|
|
/// Return the [`TxOut`] of `outpoint` if it has been indexed, and if it corresponds to a
|
|
|
|
|
/// tracked keychain.
|
|
|
|
|
///
|
|
|
|
|
/// The associated keychain and keychain index of the txout's spk is also returned.
|
|
|
|
|
///
|
|
|
|
|
/// This calls [`SpkTxOutIndex::txout`] internally.
|
|
|
|
|
pub fn txout(&self, outpoint: OutPoint) -> Option<(K, u32, &TxOut)> {
|
|
|
|
|
self.inner
|
|
|
|
|
.txout(outpoint)
|
|
|
|
|
.map(|((k, i), txo)| (k.clone(), *i, txo))
|
|
|
|
|
let ((descriptor_id, index), txo) = self.inner.txout(outpoint)?;
|
|
|
|
|
let (keychain, _) = self.descriptor_ids_to_keychain.get(descriptor_id)?;
|
|
|
|
|
Some((keychain.clone(), *index, txo))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Return the script that exists under the given `keychain`'s `index`.
|
|
|
|
|
///
|
|
|
|
|
/// This calls [`SpkTxOutIndex::spk_at_index`] internally.
|
|
|
|
|
pub fn spk_at_index(&self, keychain: K, index: u32) -> Option<&Script> {
|
|
|
|
|
self.inner.spk_at_index(&(keychain, index))
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(&keychain)?.0;
|
|
|
|
|
self.inner.spk_at_index(&(descriptor_id, index))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the keychain and keychain index associated with the spk.
|
|
|
|
|
///
|
|
|
|
|
/// This calls [`SpkTxOutIndex::index_of_spk`] internally.
|
|
|
|
|
pub fn index_of_spk(&self, script: &Script) -> Option<(K, u32)> {
|
|
|
|
|
self.inner.index_of_spk(script).cloned()
|
|
|
|
|
let (desc_id, last_index) = self.inner.index_of_spk(script)?;
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(desc_id)
|
|
|
|
|
.map(|(k, _)| (k.clone(), *last_index))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns whether the spk under the `keychain`'s `index` has been used.
|
|
|
|
|
@@ -299,7 +347,11 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// This calls [`SpkTxOutIndex::is_used`] internally.
|
|
|
|
|
pub fn is_used(&self, keychain: K, index: u32) -> bool {
|
|
|
|
|
self.inner.is_used(&(keychain, index))
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(&keychain).map(|k| k.0);
|
|
|
|
|
match descriptor_id {
|
|
|
|
|
Some(descriptor_id) => self.inner.is_used(&(descriptor_id, index)),
|
|
|
|
|
None => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Marks the script pubkey at `index` as used even though the tracker hasn't seen an output
|
|
|
|
|
@@ -307,7 +359,9 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// This only has an effect when the `index` had been added to `self` already and was unused.
|
|
|
|
|
///
|
|
|
|
|
/// Returns whether the `index` was initially present as `unused`.
|
|
|
|
|
/// Returns whether the spk under the given `keychain` and `index` is successfully
|
|
|
|
|
/// marked as used. Returns false either when there is no descriptor under the given
|
|
|
|
|
/// keychain, or when the spk is already marked as used.
|
|
|
|
|
///
|
|
|
|
|
/// This is useful when you want to reserve a script pubkey for something but don't want to add
|
|
|
|
|
/// the transaction output using it to the index yet. Other callers will consider `index` on
|
|
|
|
|
@@ -317,7 +371,11 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// [`unmark_used`]: Self::unmark_used
|
|
|
|
|
pub fn mark_used(&mut self, keychain: K, index: u32) -> bool {
|
|
|
|
|
self.inner.mark_used(&(keychain, index))
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(&keychain).map(|k| k.0);
|
|
|
|
|
match descriptor_id {
|
|
|
|
|
Some(descriptor_id) => self.inner.mark_used(&(descriptor_id, index)),
|
|
|
|
|
None => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Undoes the effect of [`mark_used`]. Returns whether the `index` is inserted back into
|
|
|
|
|
@@ -330,7 +388,11 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// [`mark_used`]: Self::mark_used
|
|
|
|
|
pub fn unmark_used(&mut self, keychain: K, index: u32) -> bool {
|
|
|
|
|
self.inner.unmark_used(&(keychain, index))
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(&keychain).map(|k| k.0);
|
|
|
|
|
match descriptor_id {
|
|
|
|
|
Some(descriptor_id) => self.inner.unmark_used(&(descriptor_id, index)),
|
|
|
|
|
None => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the total value transfer effect `tx` has on the script pubkeys belonging to the
|
|
|
|
|
@@ -344,7 +406,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
range: impl RangeBounds<K>,
|
|
|
|
|
) -> (Amount, Amount) {
|
|
|
|
|
self.inner
|
|
|
|
|
.sent_and_received(tx, Self::map_to_inner_bounds(range))
|
|
|
|
|
.sent_and_received(tx, self.map_to_inner_bounds(range))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the net value that this transaction gives to the script pubkeys in the index and
|
|
|
|
|
@@ -355,34 +417,79 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// [`sent_and_received`]: Self::sent_and_received
|
|
|
|
|
pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds<K>) -> SignedAmount {
|
|
|
|
|
self.inner.net_value(tx, Self::map_to_inner_bounds(range))
|
|
|
|
|
self.inner.net_value(tx, self.map_to_inner_bounds(range))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
/// Return a reference to the internal map of keychain to descriptors.
|
|
|
|
|
pub fn keychains(&self) -> &BTreeMap<K, Descriptor<DescriptorPublicKey>> {
|
|
|
|
|
&self.keychains
|
|
|
|
|
/// Return the map of the keychain to descriptors.
|
|
|
|
|
pub fn keychains(
|
|
|
|
|
&self,
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&K, &Descriptor<DescriptorPublicKey>)> + ExactSizeIterator + '_
|
|
|
|
|
{
|
|
|
|
|
self.keychains_to_descriptors
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(k, (_, d))| (k, d))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Add a keychain to the tracker's `txout_index` with a descriptor to derive addresses.
|
|
|
|
|
/// Insert a descriptor with a keychain associated to it.
|
|
|
|
|
///
|
|
|
|
|
/// Adding a keychain means you will be able to derive new script pubkeys under that keychain
|
|
|
|
|
/// Adding a descriptor means you will be able to derive new script pubkeys under it
|
|
|
|
|
/// and the txout index will discover transaction outputs with those script pubkeys.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// This will panic if a different `descriptor` is introduced to the same `keychain`.
|
|
|
|
|
pub fn add_keychain(&mut self, keychain: K, descriptor: Descriptor<DescriptorPublicKey>) {
|
|
|
|
|
let old_descriptor = &*self
|
|
|
|
|
.keychains
|
|
|
|
|
.entry(keychain.clone())
|
|
|
|
|
.or_insert_with(|| descriptor.clone());
|
|
|
|
|
assert_eq!(
|
|
|
|
|
&descriptor, old_descriptor,
|
|
|
|
|
"keychain already contains a different descriptor"
|
|
|
|
|
);
|
|
|
|
|
/// When trying to add a keychain that already existed under a different descriptor, or a descriptor
|
|
|
|
|
/// that already existed with a different keychain, the old keychain (or descriptor) will be
|
|
|
|
|
/// overwritten.
|
|
|
|
|
pub fn insert_descriptor(
|
|
|
|
|
&mut self,
|
|
|
|
|
keychain: K,
|
|
|
|
|
descriptor: Descriptor<DescriptorPublicKey>,
|
|
|
|
|
) -> super::ChangeSet<K> {
|
|
|
|
|
let descriptor_id = descriptor.descriptor_id();
|
|
|
|
|
// First, we fill the keychain -> (desc_id, descriptor) map
|
|
|
|
|
let old_descriptor_opt = self
|
|
|
|
|
.keychains_to_descriptors
|
|
|
|
|
.insert(keychain.clone(), (descriptor_id, descriptor.clone()));
|
|
|
|
|
|
|
|
|
|
// Then, we fill the descriptor_id -> (keychain, descriptor) map
|
|
|
|
|
let old_keychain_opt = self
|
|
|
|
|
.descriptor_ids_to_keychain
|
|
|
|
|
.insert(descriptor_id, (keychain.clone(), descriptor.clone()));
|
|
|
|
|
|
|
|
|
|
// If `keychain` already had a `descriptor` associated, different from the `descriptor`
|
|
|
|
|
// passed in, we remove it from the descriptor -> keychain map
|
|
|
|
|
if let Some((old_desc_id, _)) = old_descriptor_opt {
|
|
|
|
|
if old_desc_id != descriptor_id {
|
|
|
|
|
self.descriptor_ids_to_keychain.remove(&old_desc_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lastly, we fill the desc_id -> desc map
|
|
|
|
|
self.descriptor_ids_to_descriptors
|
|
|
|
|
.insert(descriptor_id, descriptor.clone());
|
|
|
|
|
|
|
|
|
|
self.replenish_lookahead(&keychain, self.lookahead);
|
|
|
|
|
|
|
|
|
|
// If both the keychain and descriptor were already inserted and associated, the
|
|
|
|
|
// keychains_added changeset must be empty
|
|
|
|
|
let keychains_added = if old_keychain_opt.map(|(k, _)| k) == Some(keychain.clone())
|
|
|
|
|
&& old_descriptor_opt.map(|(_, d)| d) == Some(descriptor.clone())
|
|
|
|
|
{
|
|
|
|
|
[].into()
|
|
|
|
|
} else {
|
|
|
|
|
[(keychain, descriptor)].into()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
super::ChangeSet {
|
|
|
|
|
keychains_added,
|
|
|
|
|
last_revealed: [].into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the descriptor associated with the keychain. Returns `None` if the keychain doesn't
|
|
|
|
|
/// have a descriptor associated with it.
|
|
|
|
|
pub fn get_descriptor(&self, keychain: &K) -> Option<&Descriptor<DescriptorPublicKey>> {
|
|
|
|
|
self.keychains_to_descriptors.get(keychain).map(|(_, d)| d)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the lookahead setting.
|
|
|
|
|
@@ -398,63 +505,60 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// This does not change the global `lookahead` setting.
|
|
|
|
|
pub fn lookahead_to_target(&mut self, keychain: &K, target_index: u32) {
|
|
|
|
|
let (next_index, _) = self.next_index(keychain);
|
|
|
|
|
if let Some((next_index, _)) = self.next_index(keychain) {
|
|
|
|
|
let temp_lookahead = (target_index + 1)
|
|
|
|
|
.checked_sub(next_index)
|
|
|
|
|
.filter(|&index| index > 0);
|
|
|
|
|
|
|
|
|
|
let temp_lookahead = (target_index + 1)
|
|
|
|
|
.checked_sub(next_index)
|
|
|
|
|
.filter(|&index| index > 0);
|
|
|
|
|
|
|
|
|
|
if let Some(temp_lookahead) = temp_lookahead {
|
|
|
|
|
self.replenish_lookahead(keychain, temp_lookahead);
|
|
|
|
|
if let Some(temp_lookahead) = temp_lookahead {
|
|
|
|
|
self.replenish_lookahead(keychain, temp_lookahead);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn replenish_lookahead(&mut self, keychain: &K, lookahead: u32) {
|
|
|
|
|
let descriptor = self.keychains.get(keychain).expect("keychain must exist");
|
|
|
|
|
let next_store_index = self.next_store_index(keychain);
|
|
|
|
|
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
|
|
|
|
|
let descriptor_opt = self.keychains_to_descriptors.get(keychain).cloned();
|
|
|
|
|
if let Some((descriptor_id, descriptor)) = descriptor_opt {
|
|
|
|
|
let next_store_index = self.next_store_index(descriptor_id);
|
|
|
|
|
let next_reveal_index = self.last_revealed.get(&descriptor_id).map_or(0, |v| *v + 1);
|
|
|
|
|
|
|
|
|
|
for (new_index, new_spk) in
|
|
|
|
|
SpkIterator::new_with_range(descriptor, next_store_index..next_reveal_index + lookahead)
|
|
|
|
|
{
|
|
|
|
|
let _inserted = self
|
|
|
|
|
.inner
|
|
|
|
|
.insert_spk((keychain.clone(), new_index), new_spk);
|
|
|
|
|
debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_store_index={}, next_reveal_index={}", keychain, lookahead, next_store_index, next_reveal_index);
|
|
|
|
|
for (new_index, new_spk) in SpkIterator::new_with_range(
|
|
|
|
|
descriptor,
|
|
|
|
|
next_store_index..next_reveal_index + lookahead,
|
|
|
|
|
) {
|
|
|
|
|
let _inserted = self.inner.insert_spk((descriptor_id, new_index), new_spk);
|
|
|
|
|
debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_store_index={}, next_reveal_index={}", keychain, lookahead, next_store_index, next_reveal_index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn next_store_index(&self, keychain: &K) -> u32 {
|
|
|
|
|
fn next_store_index(&self, descriptor_id: DescriptorId) -> u32 {
|
|
|
|
|
self.inner()
|
|
|
|
|
.all_spks()
|
|
|
|
|
// This range is filtering out the spks with a keychain different than
|
|
|
|
|
// `keychain`. We don't use filter here as range is more optimized.
|
|
|
|
|
.range((keychain.clone(), u32::MIN)..(keychain.clone(), u32::MAX))
|
|
|
|
|
// This range is keeping only the spks with descriptor_id equal to
|
|
|
|
|
// `descriptor_id`. We don't use filter here as range is more optimized.
|
|
|
|
|
.range((descriptor_id, u32::MIN)..(descriptor_id, u32::MAX))
|
|
|
|
|
.last()
|
|
|
|
|
.map_or(0, |((_, index), _)| *index + 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get an unbounded spk iterator over a given `keychain`.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// This will panic if the given `keychain`'s descriptor does not exist.
|
|
|
|
|
pub fn unbounded_spk_iter(&self, keychain: &K) -> SpkIterator<Descriptor<DescriptorPublicKey>> {
|
|
|
|
|
SpkIterator::new(
|
|
|
|
|
self.keychains
|
|
|
|
|
.get(keychain)
|
|
|
|
|
.expect("keychain does not exist")
|
|
|
|
|
.clone(),
|
|
|
|
|
)
|
|
|
|
|
/// Get an unbounded spk iterator over a given `keychain`. Returns `None` if the provided
|
|
|
|
|
/// keychain doesn't exist
|
|
|
|
|
pub fn unbounded_spk_iter(
|
|
|
|
|
&self,
|
|
|
|
|
keychain: &K,
|
|
|
|
|
) -> Option<SpkIterator<Descriptor<DescriptorPublicKey>>> {
|
|
|
|
|
let descriptor = self.keychains_to_descriptors.get(keychain)?.1.clone();
|
|
|
|
|
Some(SpkIterator::new(descriptor))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get unbounded spk iterators for all keychains.
|
|
|
|
|
pub fn all_unbounded_spk_iters(
|
|
|
|
|
&self,
|
|
|
|
|
) -> BTreeMap<K, SpkIterator<Descriptor<DescriptorPublicKey>>> {
|
|
|
|
|
self.keychains
|
|
|
|
|
self.keychains_to_descriptors
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(k, descriptor)| (k.clone(), SpkIterator::new(descriptor.clone())))
|
|
|
|
|
.map(|(k, (_, descriptor))| (k.clone(), SpkIterator::new(descriptor.clone())))
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -463,18 +567,30 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
&self,
|
|
|
|
|
range: impl RangeBounds<K>,
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&K, u32, &Script)> + Clone {
|
|
|
|
|
self.keychains.range(range).flat_map(|(keychain, _)| {
|
|
|
|
|
let start = Bound::Included((keychain.clone(), u32::MIN));
|
|
|
|
|
let end = match self.last_revealed.get(keychain) {
|
|
|
|
|
Some(last_revealed) => Bound::Included((keychain.clone(), *last_revealed)),
|
|
|
|
|
None => Bound::Excluded((keychain.clone(), u32::MIN)),
|
|
|
|
|
};
|
|
|
|
|
self.keychains_to_descriptors
|
|
|
|
|
.range(range)
|
|
|
|
|
.flat_map(|(_, (descriptor_id, _))| {
|
|
|
|
|
let start = Bound::Included((*descriptor_id, u32::MIN));
|
|
|
|
|
let end = match self.last_revealed.get(descriptor_id) {
|
|
|
|
|
Some(last_revealed) => Bound::Included((*descriptor_id, *last_revealed)),
|
|
|
|
|
None => Bound::Excluded((*descriptor_id, u32::MIN)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.inner
|
|
|
|
|
.all_spks()
|
|
|
|
|
.range((start, end))
|
|
|
|
|
.map(|((keychain, i), spk)| (keychain, *i, spk.as_script()))
|
|
|
|
|
})
|
|
|
|
|
self.inner
|
|
|
|
|
.all_spks()
|
|
|
|
|
.range((start, end))
|
|
|
|
|
.map(|((descriptor_id, i), spk)| {
|
|
|
|
|
(
|
|
|
|
|
&self
|
|
|
|
|
.descriptor_ids_to_keychain
|
|
|
|
|
.get(descriptor_id)
|
|
|
|
|
.expect("Must be here")
|
|
|
|
|
.0,
|
|
|
|
|
*i,
|
|
|
|
|
spk.as_script(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iterate over revealed spks of the given `keychain`.
|
|
|
|
|
@@ -488,20 +604,29 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
|
|
|
|
|
/// Iterate over revealed, but unused, spks of all keychains.
|
|
|
|
|
pub fn unused_spks(&self) -> impl DoubleEndedIterator<Item = (K, u32, &Script)> + Clone {
|
|
|
|
|
self.keychains.keys().flat_map(|keychain| {
|
|
|
|
|
self.keychains_to_descriptors.keys().flat_map(|keychain| {
|
|
|
|
|
self.unused_keychain_spks(keychain)
|
|
|
|
|
.map(|(i, spk)| (keychain.clone(), i, spk))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iterate over revealed, but unused, spks of the given `keychain`.
|
|
|
|
|
/// Returns an empty iterator if the provided keychain doesn't exist.
|
|
|
|
|
pub fn unused_keychain_spks(
|
|
|
|
|
&self,
|
|
|
|
|
keychain: &K,
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (u32, &Script)> + Clone {
|
|
|
|
|
let next_i = self.last_revealed.get(keychain).map_or(0, |&i| i + 1);
|
|
|
|
|
let desc_id = self
|
|
|
|
|
.keychains_to_descriptors
|
|
|
|
|
.get(keychain)
|
|
|
|
|
.map(|(desc_id, _)| *desc_id)
|
|
|
|
|
// We use a dummy desc id if we can't find the real one in our map. In this way,
|
|
|
|
|
// if this method was to be called with a non-existent keychain, we would return an
|
|
|
|
|
// empty iterator
|
|
|
|
|
.unwrap_or_else(|| DescriptorId::from_byte_array([0; 32]));
|
|
|
|
|
let next_i = self.last_revealed.get(&desc_id).map_or(0, |&i| i + 1);
|
|
|
|
|
self.inner
|
|
|
|
|
.unused_spks((keychain.clone(), u32::MIN)..(keychain.clone(), next_i))
|
|
|
|
|
.unused_spks((desc_id, u32::MIN)..(desc_id, next_i))
|
|
|
|
|
.map(|((_, i), spk)| (*i, spk))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -516,17 +641,15 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
///
|
|
|
|
|
/// Not checking the second field of the tuple may result in address reuse.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Panics if the `keychain` does not exist.
|
|
|
|
|
pub fn next_index(&self, keychain: &K) -> (u32, bool) {
|
|
|
|
|
let descriptor = self.keychains.get(keychain).expect("keychain must exist");
|
|
|
|
|
let last_index = self.last_revealed.get(keychain).cloned();
|
|
|
|
|
/// Returns None if the provided `keychain` doesn't exist.
|
|
|
|
|
pub fn next_index(&self, keychain: &K) -> Option<(u32, bool)> {
|
|
|
|
|
let (descriptor_id, descriptor) = self.keychains_to_descriptors.get(keychain)?;
|
|
|
|
|
let last_index = self.last_revealed.get(descriptor_id).cloned();
|
|
|
|
|
|
|
|
|
|
// we can only get the next index if the wildcard exists.
|
|
|
|
|
let has_wildcard = descriptor.has_wildcard();
|
|
|
|
|
|
|
|
|
|
match last_index {
|
|
|
|
|
Some(match last_index {
|
|
|
|
|
// if there is no index, next_index is always 0.
|
|
|
|
|
None => (0, true),
|
|
|
|
|
// descriptors without wildcards can only have one index.
|
|
|
|
|
@@ -538,19 +661,28 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
Some(index) if index == BIP32_MAX_INDEX => (index, false),
|
|
|
|
|
// get the next derivation index.
|
|
|
|
|
Some(index) => (index + 1, true),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the last derivation index that is revealed for each keychain.
|
|
|
|
|
///
|
|
|
|
|
/// Keychains with no revealed indices will not be included in the returned [`BTreeMap`].
|
|
|
|
|
pub fn last_revealed_indices(&self) -> &BTreeMap<K, u32> {
|
|
|
|
|
&self.last_revealed
|
|
|
|
|
pub fn last_revealed_indices(&self) -> BTreeMap<K, u32> {
|
|
|
|
|
self.last_revealed
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|(descriptor_id, index)| {
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(descriptor_id)
|
|
|
|
|
.map(|(k, _)| (k.clone(), *index))
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the last derivation index revealed for `keychain`.
|
|
|
|
|
/// Get the last derivation index revealed for `keychain`. Returns None if the keychain doesn't
|
|
|
|
|
/// exist, or if the keychain doesn't have any revealed scripts.
|
|
|
|
|
pub fn last_revealed_index(&self, keychain: &K) -> Option<u32> {
|
|
|
|
|
self.last_revealed.get(keychain).cloned()
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(keychain)?.0;
|
|
|
|
|
self.last_revealed.get(&descriptor_id).cloned()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Convenience method to call [`Self::reveal_to_target`] on multiple keychains.
|
|
|
|
|
@@ -565,16 +697,77 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
let mut spks = BTreeMap::new();
|
|
|
|
|
|
|
|
|
|
for (keychain, &index) in keychains {
|
|
|
|
|
let (new_spks, new_changeset) = self.reveal_to_target(keychain, index);
|
|
|
|
|
if !new_changeset.is_empty() {
|
|
|
|
|
spks.insert(keychain.clone(), new_spks);
|
|
|
|
|
changeset.append(new_changeset.clone());
|
|
|
|
|
if let Some((new_spks, new_changeset)) = self.reveal_to_target(keychain, index) {
|
|
|
|
|
if !new_changeset.is_empty() {
|
|
|
|
|
spks.insert(keychain.clone(), new_spks);
|
|
|
|
|
changeset.append(new_changeset.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(spks, changeset)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Convenience method to call `reveal_to_target` with a descriptor_id instead of a keychain.
|
|
|
|
|
/// This is useful for revealing spks of descriptors for which we don't have a keychain
|
|
|
|
|
/// tracked.
|
|
|
|
|
/// Refer to the `reveal_to_target` documentation for more.
|
|
|
|
|
///
|
|
|
|
|
/// Returns None if the provided `descriptor_id` doesn't correspond to a tracked descriptor.
|
|
|
|
|
fn reveal_to_target_with_id(
|
|
|
|
|
&mut self,
|
|
|
|
|
descriptor_id: DescriptorId,
|
|
|
|
|
target_index: u32,
|
|
|
|
|
) -> Option<(
|
|
|
|
|
SpkIterator<Descriptor<DescriptorPublicKey>>,
|
|
|
|
|
super::ChangeSet<K>,
|
|
|
|
|
)> {
|
|
|
|
|
let descriptor = self
|
|
|
|
|
.descriptor_ids_to_descriptors
|
|
|
|
|
.get(&descriptor_id)?
|
|
|
|
|
.clone();
|
|
|
|
|
let has_wildcard = descriptor.has_wildcard();
|
|
|
|
|
|
|
|
|
|
let target_index = if has_wildcard { target_index } else { 0 };
|
|
|
|
|
let next_reveal_index = self
|
|
|
|
|
.last_revealed
|
|
|
|
|
.get(&descriptor_id)
|
|
|
|
|
.map_or(0, |index| *index + 1);
|
|
|
|
|
|
|
|
|
|
debug_assert!(next_reveal_index + self.lookahead >= self.next_store_index(descriptor_id));
|
|
|
|
|
|
|
|
|
|
// If the target_index is already revealed, we are done
|
|
|
|
|
if next_reveal_index > target_index {
|
|
|
|
|
return Some((
|
|
|
|
|
SpkIterator::new_with_range(descriptor, next_reveal_index..next_reveal_index),
|
|
|
|
|
super::ChangeSet::default(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We range over the indexes that are not stored and insert their spks in the index.
|
|
|
|
|
// Indexes from next_reveal_index to next_reveal_index + lookahead are already stored (due
|
|
|
|
|
// to lookahead), so we only range from next_reveal_index + lookahead to target + lookahead
|
|
|
|
|
let range = next_reveal_index + self.lookahead..=target_index + self.lookahead;
|
|
|
|
|
for (new_index, new_spk) in SpkIterator::new_with_range(descriptor.clone(), range) {
|
|
|
|
|
let _inserted = self.inner.insert_spk((descriptor_id, new_index), new_spk);
|
|
|
|
|
debug_assert!(_inserted, "must not have existing spk");
|
|
|
|
|
debug_assert!(
|
|
|
|
|
has_wildcard || new_index == 0,
|
|
|
|
|
"non-wildcard descriptors must not iterate past index 0"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _old_index = self.last_revealed.insert(descriptor_id, target_index);
|
|
|
|
|
debug_assert!(_old_index < Some(target_index));
|
|
|
|
|
Some((
|
|
|
|
|
SpkIterator::new_with_range(descriptor, next_reveal_index..target_index + 1),
|
|
|
|
|
super::ChangeSet {
|
|
|
|
|
keychains_added: BTreeMap::new(),
|
|
|
|
|
last_revealed: core::iter::once((descriptor_id, target_index)).collect(),
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Reveals script pubkeys of the `keychain`'s descriptor **up to and including** the
|
|
|
|
|
/// `target_index`.
|
|
|
|
|
///
|
|
|
|
|
@@ -586,84 +779,46 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
/// [`super::ChangeSet`], which reports updates to the latest revealed index. If no new script
|
|
|
|
|
/// pubkeys are revealed, then both of these will be empty.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Panics if `keychain` does not exist.
|
|
|
|
|
/// Returns None if the provided `keychain` doesn't exist.
|
|
|
|
|
pub fn reveal_to_target(
|
|
|
|
|
&mut self,
|
|
|
|
|
keychain: &K,
|
|
|
|
|
target_index: u32,
|
|
|
|
|
) -> (
|
|
|
|
|
) -> Option<(
|
|
|
|
|
SpkIterator<Descriptor<DescriptorPublicKey>>,
|
|
|
|
|
super::ChangeSet<K>,
|
|
|
|
|
) {
|
|
|
|
|
let descriptor = self.keychains.get(keychain).expect("keychain must exist");
|
|
|
|
|
let has_wildcard = descriptor.has_wildcard();
|
|
|
|
|
|
|
|
|
|
let target_index = if has_wildcard { target_index } else { 0 };
|
|
|
|
|
let next_reveal_index = self
|
|
|
|
|
.last_revealed
|
|
|
|
|
.get(keychain)
|
|
|
|
|
.map_or(0, |index| *index + 1);
|
|
|
|
|
|
|
|
|
|
debug_assert!(next_reveal_index + self.lookahead >= self.next_store_index(keychain));
|
|
|
|
|
|
|
|
|
|
// If the target_index is already revealed, we are done
|
|
|
|
|
if next_reveal_index > target_index {
|
|
|
|
|
return (
|
|
|
|
|
SpkIterator::new_with_range(
|
|
|
|
|
descriptor.clone(),
|
|
|
|
|
next_reveal_index..next_reveal_index,
|
|
|
|
|
),
|
|
|
|
|
super::ChangeSet::default(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We range over the indexes that are not stored and insert their spks in the index.
|
|
|
|
|
// Indexes from next_reveal_index to next_reveal_index + lookahead are already stored (due
|
|
|
|
|
// to lookahead), so we only range from next_reveal_index + lookahead to target + lookahead
|
|
|
|
|
let range = next_reveal_index + self.lookahead..=target_index + self.lookahead;
|
|
|
|
|
for (new_index, new_spk) in SpkIterator::new_with_range(descriptor, range) {
|
|
|
|
|
let _inserted = self
|
|
|
|
|
.inner
|
|
|
|
|
.insert_spk((keychain.clone(), new_index), new_spk);
|
|
|
|
|
debug_assert!(_inserted, "must not have existing spk");
|
|
|
|
|
debug_assert!(
|
|
|
|
|
has_wildcard || new_index == 0,
|
|
|
|
|
"non-wildcard descriptors must not iterate past index 0"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _old_index = self.last_revealed.insert(keychain.clone(), target_index);
|
|
|
|
|
debug_assert!(_old_index < Some(target_index));
|
|
|
|
|
(
|
|
|
|
|
SpkIterator::new_with_range(descriptor.clone(), next_reveal_index..target_index + 1),
|
|
|
|
|
super::ChangeSet(core::iter::once((keychain.clone(), target_index)).collect()),
|
|
|
|
|
)
|
|
|
|
|
)> {
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(keychain)?.0;
|
|
|
|
|
self.reveal_to_target_with_id(descriptor_id, target_index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Attempts to reveal the next script pubkey for `keychain`.
|
|
|
|
|
///
|
|
|
|
|
/// Returns the derivation index of the revealed script pubkey, the revealed script pubkey and a
|
|
|
|
|
/// [`super::ChangeSet`] which represents changes in the last revealed index (if any).
|
|
|
|
|
/// Returns None if the provided keychain doesn't exist.
|
|
|
|
|
///
|
|
|
|
|
/// When a new script cannot be revealed, we return the last revealed script and an empty
|
|
|
|
|
/// [`super::ChangeSet`]. There are two scenarios when a new script pubkey cannot be derived:
|
|
|
|
|
///
|
|
|
|
|
/// 1. The descriptor has no wildcard and already has one script revealed.
|
|
|
|
|
/// 2. The descriptor has already revealed scripts up to the numeric bound.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Panics if the `keychain` does not exist.
|
|
|
|
|
pub fn reveal_next_spk(&mut self, keychain: &K) -> ((u32, &Script), super::ChangeSet<K>) {
|
|
|
|
|
let (next_index, _) = self.next_index(keychain);
|
|
|
|
|
let changeset = self.reveal_to_target(keychain, next_index).1;
|
|
|
|
|
/// 3. There is no descriptor associated with the given keychain.
|
|
|
|
|
pub fn reveal_next_spk(
|
|
|
|
|
&mut self,
|
|
|
|
|
keychain: &K,
|
|
|
|
|
) -> Option<((u32, &Script), super::ChangeSet<K>)> {
|
|
|
|
|
let descriptor_id = self.keychains_to_descriptors.get(keychain)?.0;
|
|
|
|
|
let (next_index, _) = self.next_index(keychain).expect("We know keychain exists");
|
|
|
|
|
let changeset = self
|
|
|
|
|
.reveal_to_target(keychain, next_index)
|
|
|
|
|
.expect("We know keychain exists")
|
|
|
|
|
.1;
|
|
|
|
|
let script = self
|
|
|
|
|
.inner
|
|
|
|
|
.spk_at_index(&(keychain.clone(), next_index))
|
|
|
|
|
.spk_at_index(&(descriptor_id, next_index))
|
|
|
|
|
.expect("script must already be stored");
|
|
|
|
|
((next_index, script), changeset)
|
|
|
|
|
Some(((next_index, script), changeset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the next unused script pubkey in the keychain. I.e., the script pubkey with the lowest
|
|
|
|
|
@@ -675,21 +830,22 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
/// has used all scripts up to the derivation bounds, then the last derived script pubkey will be
|
|
|
|
|
/// returned.
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Panics if `keychain` has never been added to the index
|
|
|
|
|
pub fn next_unused_spk(&mut self, keychain: &K) -> ((u32, &Script), super::ChangeSet<K>) {
|
|
|
|
|
/// Returns None if the provided keychain doesn't exist.
|
|
|
|
|
pub fn next_unused_spk(
|
|
|
|
|
&mut self,
|
|
|
|
|
keychain: &K,
|
|
|
|
|
) -> Option<((u32, &Script), super::ChangeSet<K>)> {
|
|
|
|
|
let need_new = self.unused_keychain_spks(keychain).next().is_none();
|
|
|
|
|
// this rather strange branch is needed because of some lifetime issues
|
|
|
|
|
if need_new {
|
|
|
|
|
self.reveal_next_spk(keychain)
|
|
|
|
|
} else {
|
|
|
|
|
(
|
|
|
|
|
Some((
|
|
|
|
|
self.unused_keychain_spks(keychain)
|
|
|
|
|
.next()
|
|
|
|
|
.expect("we already know next exists"),
|
|
|
|
|
super::ChangeSet::default(),
|
|
|
|
|
)
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -708,21 +864,40 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
&'a self,
|
|
|
|
|
range: impl RangeBounds<K> + 'a,
|
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&'a K, u32, OutPoint)> + 'a {
|
|
|
|
|
let bounds = Self::map_to_inner_bounds(range);
|
|
|
|
|
let bounds = self.map_to_inner_bounds(range);
|
|
|
|
|
self.inner
|
|
|
|
|
.outputs_in_range(bounds)
|
|
|
|
|
.map(move |((keychain, i), op)| (keychain, *i, op))
|
|
|
|
|
.map(move |((descriptor_id, i), op)| {
|
|
|
|
|
(
|
|
|
|
|
&self
|
|
|
|
|
.descriptor_ids_to_keychain
|
|
|
|
|
.get(descriptor_id)
|
|
|
|
|
.expect("must be here")
|
|
|
|
|
.0,
|
|
|
|
|
*i,
|
|
|
|
|
op,
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn map_to_inner_bounds(bound: impl RangeBounds<K>) -> impl RangeBounds<(K, u32)> {
|
|
|
|
|
fn map_to_inner_bounds(
|
|
|
|
|
&self,
|
|
|
|
|
bound: impl RangeBounds<K>,
|
|
|
|
|
) -> impl RangeBounds<(DescriptorId, u32)> {
|
|
|
|
|
let get_desc_id = |keychain| {
|
|
|
|
|
self.keychains_to_descriptors
|
|
|
|
|
.get(keychain)
|
|
|
|
|
.map(|(desc_id, _)| *desc_id)
|
|
|
|
|
.unwrap_or_else(|| DescriptorId::from_byte_array([0; 32]))
|
|
|
|
|
};
|
|
|
|
|
let start = match bound.start_bound() {
|
|
|
|
|
Bound::Included(keychain) => Bound::Included((keychain.clone(), u32::MIN)),
|
|
|
|
|
Bound::Excluded(keychain) => Bound::Excluded((keychain.clone(), u32::MAX)),
|
|
|
|
|
Bound::Included(keychain) => Bound::Included((get_desc_id(keychain), u32::MIN)),
|
|
|
|
|
Bound::Excluded(keychain) => Bound::Excluded((get_desc_id(keychain), u32::MAX)),
|
|
|
|
|
Bound::Unbounded => Bound::Unbounded,
|
|
|
|
|
};
|
|
|
|
|
let end = match bound.end_bound() {
|
|
|
|
|
Bound::Included(keychain) => Bound::Included((keychain.clone(), u32::MAX)),
|
|
|
|
|
Bound::Excluded(keychain) => Bound::Excluded((keychain.clone(), u32::MIN)),
|
|
|
|
|
Bound::Included(keychain) => Bound::Included((get_desc_id(keychain), u32::MAX)),
|
|
|
|
|
Bound::Excluded(keychain) => Bound::Excluded((get_desc_id(keychain), u32::MIN)),
|
|
|
|
|
Bound::Unbounded => Bound::Unbounded,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -738,7 +913,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
/// Returns the highest derivation index of each keychain that [`KeychainTxOutIndex`] has found
|
|
|
|
|
/// a [`TxOut`] with it's script pubkey.
|
|
|
|
|
pub fn last_used_indices(&self) -> BTreeMap<K, u32> {
|
|
|
|
|
self.keychains
|
|
|
|
|
self.keychains_to_descriptors
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|(keychain, _)| {
|
|
|
|
|
self.last_used_index(keychain)
|
|
|
|
|
@@ -747,9 +922,28 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Applies the derivation changeset to the [`KeychainTxOutIndex`], extending the number of
|
|
|
|
|
/// derived scripts per keychain, as specified in the `changeset`.
|
|
|
|
|
/// Applies the derivation changeset to the [`KeychainTxOutIndex`], as specified in the
|
|
|
|
|
/// [`ChangeSet::append`] documentation:
|
|
|
|
|
/// - Extends the number of derived scripts per keychain
|
|
|
|
|
/// - Adds new descriptors introduced
|
|
|
|
|
/// - If a descriptor is introduced for a keychain that already had a descriptor, overwrites
|
|
|
|
|
/// the old descriptor
|
|
|
|
|
pub fn apply_changeset(&mut self, changeset: super::ChangeSet<K>) {
|
|
|
|
|
let _ = self.reveal_to_target_multi(&changeset.0);
|
|
|
|
|
let ChangeSet {
|
|
|
|
|
keychains_added,
|
|
|
|
|
last_revealed,
|
|
|
|
|
} = changeset;
|
|
|
|
|
for (keychain, descriptor) in keychains_added {
|
|
|
|
|
let _ = self.insert_descriptor(keychain, descriptor);
|
|
|
|
|
}
|
|
|
|
|
let last_revealed = last_revealed
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|(descriptor_id, index)| {
|
|
|
|
|
self.descriptor_ids_to_keychain
|
|
|
|
|
.get(&descriptor_id)
|
|
|
|
|
.map(|(k, _)| (k.clone(), index))
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let _ = self.reveal_to_target_multi(&last_revealed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|