diff --git a/crates/chain/src/indexed_tx_graph.rs b/crates/chain/src/indexed_tx_graph.rs index c2b83600b..cef0bbf81 100644 --- a/crates/chain/src/indexed_tx_graph.rs +++ b/crates/chain/src/indexed_tx_graph.rs @@ -320,6 +320,7 @@ impl From> for ChangeSet { } } +#[cfg(feature = "miniscript")] impl From> for ChangeSet> { fn from(indexer: keychain::ChangeSet) -> Self { Self { diff --git a/crates/chain/src/keychain.rs b/crates/chain/src/keychain.rs index 240d944ef..b822dc901 100644 --- a/crates/chain/src/keychain.rs +++ b/crates/chain/src/keychain.rs @@ -10,78 +10,12 @@ //! //! [`SpkTxOutIndex`]: crate::SpkTxOutIndex -use crate::{collections::BTreeMap, Append}; - #[cfg(feature = "miniscript")] mod txout_index; use bitcoin::Amount; #[cfg(feature = "miniscript")] pub use txout_index::*; -/// Represents updates to the derivation index of a [`KeychainTxOutIndex`]. -/// It maps each keychain `K` to its last revealed index. -/// -/// It can be applied to [`KeychainTxOutIndex`] with [`apply_changeset`]. [`ChangeSet`]s are -/// monotone in that they will never decrease the revealed derivation index. -/// -/// [`KeychainTxOutIndex`]: crate::keychain::KeychainTxOutIndex -/// [`apply_changeset`]: crate::keychain::KeychainTxOutIndex::apply_changeset -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde( - crate = "serde_crate", - bound( - deserialize = "K: Ord + serde::Deserialize<'de>", - serialize = "K: Ord + serde::Serialize" - ) - ) -)] -#[must_use] -pub struct ChangeSet(pub BTreeMap); - -impl ChangeSet { - /// Get the inner map of the keychain to its new derivation index. - pub fn as_inner(&self) -> &BTreeMap { - &self.0 - } -} - -impl Append for ChangeSet { - /// Append another [`ChangeSet`] into self. - /// - /// 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) { - *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); - } - - /// Returns whether the changeset are empty. - fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl Default for ChangeSet { - fn default() -> Self { - Self(Default::default()) - } -} - -impl AsRef> for ChangeSet { - fn as_ref(&self) -> &BTreeMap { - &self.0 - } -} - /// Balance, differentiated into various categories. #[derive(Debug, PartialEq, Eq, Clone, Default)] #[cfg_attr( @@ -137,40 +71,3 @@ impl core::ops::Add for Balance { } } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn append_keychain_derivation_indices() { - #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)] - enum Keychain { - One, - Two, - Three, - Four, - } - let mut lhs_di = BTreeMap::::default(); - let mut rhs_di = BTreeMap::::default(); - lhs_di.insert(Keychain::One, 7); - lhs_di.insert(Keychain::Two, 0); - rhs_di.insert(Keychain::One, 3); - rhs_di.insert(Keychain::Two, 5); - lhs_di.insert(Keychain::Three, 3); - rhs_di.insert(Keychain::Four, 4); - - let mut lhs = ChangeSet(lhs_di); - let rhs = ChangeSet(rhs_di); - lhs.append(rhs); - - // Exiting index doesn't update if the new index in `other` is lower than `self`. - assert_eq!(lhs.0.get(&Keychain::One), Some(&7)); - // Existing index updates if the new index in `other` is higher than `self`. - assert_eq!(lhs.0.get(&Keychain::Two), Some(&5)); - // Existing index is unchanged if keychain doesn't exist in `other`. - assert_eq!(lhs.0.get(&Keychain::Three), Some(&3)); - // New keychain gets added if the keychain is in `other` but not in `self`. - assert_eq!(lhs.0.get(&Keychain::Four), Some(&4)); - } -} diff --git a/crates/chain/src/keychain/txout_index.rs b/crates/chain/src/keychain/txout_index.rs index 789db6c6f..dd83453a4 100644 --- a/crates/chain/src/keychain/txout_index.rs +++ b/crates/chain/src/keychain/txout_index.rs @@ -13,6 +13,71 @@ 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 can be applied to [`KeychainTxOutIndex`] with [`apply_changeset`]. [`ChangeSet] are +/// monotone in that they will never decrease the revealed derivation index. +/// +/// [`KeychainTxOutIndex`]: crate::keychain::KeychainTxOutIndex +/// [`apply_changeset`]: crate::keychain::KeychainTxOutIndex::apply_changeset +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde( + crate = "serde_crate", + bound( + deserialize = "K: Ord + serde::Deserialize<'de>", + serialize = "K: Ord + serde::Serialize" + ) + ) +)] +#[must_use] +pub struct ChangeSet(pub BTreeMap); + +impl ChangeSet { + /// Get the inner map of the keychain to its new derivation index. + pub fn as_inner(&self) -> &BTreeMap { + &self.0 + } +} + +impl Append for ChangeSet { + /// Append another [`ChangeSet`] into self. + /// + /// 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) { + *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); + } + + /// Returns whether the changeset are empty. + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl Default for ChangeSet { + fn default() -> Self { + Self(Default::default()) + } +} + +impl AsRef> for ChangeSet { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } +} + const DEFAULT_LOOKAHEAD: u32 = 25; /// [`KeychainTxOutIndex`] controls how script pubkeys are revealed for multiple keychains, and diff --git a/crates/chain/tests/test_keychain_txout_index.rs b/crates/chain/tests/test_keychain_txout_index.rs index 6e67a0f29..1bf40df1f 100644 --- a/crates/chain/tests/test_keychain_txout_index.rs +++ b/crates/chain/tests/test_keychain_txout_index.rs @@ -5,7 +5,7 @@ mod common; use bdk_chain::{ collections::BTreeMap, indexed_tx_graph::Indexer, - keychain::{self, KeychainTxOutIndex}, + keychain::{self, ChangeSet, KeychainTxOutIndex}, Append, }; @@ -44,6 +44,38 @@ fn spk_at_index(descriptor: &Descriptor, index: u32) -> Scr .script_pubkey() } +#[test] +fn append_keychain_derivation_indices() { + #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)] + enum Keychain { + One, + Two, + Three, + Four, + } + let mut lhs_di = BTreeMap::::default(); + let mut rhs_di = BTreeMap::::default(); + lhs_di.insert(Keychain::One, 7); + lhs_di.insert(Keychain::Two, 0); + rhs_di.insert(Keychain::One, 3); + rhs_di.insert(Keychain::Two, 5); + lhs_di.insert(Keychain::Three, 3); + rhs_di.insert(Keychain::Four, 4); + + let mut lhs = ChangeSet(lhs_di); + let rhs = ChangeSet(rhs_di); + lhs.append(rhs); + + // Exiting index doesn't update if the new index in `other` is lower than `self`. + assert_eq!(lhs.0.get(&Keychain::One), Some(&7)); + // Existing index updates if the new index in `other` is higher than `self`. + assert_eq!(lhs.0.get(&Keychain::Two), Some(&5)); + // Existing index is unchanged if keychain doesn't exist in `other`. + assert_eq!(lhs.0.get(&Keychain::Three), Some(&3)); + // New keychain gets added if the keychain is in `other` but not in `self`. + assert_eq!(lhs.0.get(&Keychain::Four), Some(&4)); +} + #[test] fn test_set_all_derivation_indices() { use bdk_chain::indexed_tx_graph::Indexer;