diff --git a/crates/chain/src/indexed_tx_graph.rs b/crates/chain/src/indexed_tx_graph.rs index 2e0315d8..0b27150c 100644 --- a/crates/chain/src/indexed_tx_graph.rs +++ b/crates/chain/src/indexed_tx_graph.rs @@ -1,12 +1,12 @@ -use core::convert::Infallible; +use core::{convert::Infallible, ops::AddAssign}; -use bitcoin::{OutPoint, Transaction, TxOut}; +use bitcoin::{OutPoint, Script, Transaction, TxOut}; use crate::{ keychain::Balance, sparse_chain::ChainPosition, tx_graph::{Additions, TxGraph, TxNode}, - BlockAnchor, ChainOracle, FullTxOut, ObservedIn, TxIndex, TxIndexAdditions, + BlockAnchor, ChainOracle, FullTxOut, ObservedIn, TxIndex, }; /// An outwards-facing view of a transaction that is part of the *best chain*'s history. @@ -18,46 +18,37 @@ pub struct TxInChain<'a, T, A> { pub tx: TxNode<'a, T, A>, } -/// An outwards-facing view of a relevant txout that is part of the *best chain*'s history. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TxOutInChain<'a, I, A> { - /// The custom index of the txout's script pubkey. - pub spk_index: &'a I, - /// The full txout. - pub txout: FullTxOut>, -} - /// A structure that represents changes to an [`IndexedTxGraph`]. #[derive(Clone, Debug, PartialEq)] #[must_use] -pub struct IndexedAdditions { +pub struct IndexedAdditions { /// [`TxGraph`] additions. pub graph_additions: Additions, /// [`TxIndex`] additions. - pub index_delta: D, + pub index_additions: IA, /// Last block height witnessed (if any). pub last_height: Option, } -impl Default for IndexedAdditions { +impl Default for IndexedAdditions { fn default() -> Self { Self { graph_additions: Default::default(), - index_delta: Default::default(), + index_additions: Default::default(), last_height: None, } } } -impl TxIndexAdditions for IndexedAdditions { - fn append_additions(&mut self, other: Self) { +impl AddAssign for IndexedAdditions { + fn add_assign(&mut self, rhs: Self) { let Self { graph_additions, - index_delta, + index_additions: index_delta, last_height, - } = other; + } = rhs; self.graph_additions.append(graph_additions); - self.index_delta.append_additions(index_delta); + self.index_additions += index_delta; if self.last_height < last_height { let last_height = last_height.expect("must exist as it is larger than self.last_height"); @@ -102,11 +93,11 @@ impl IndexedTxGraph { pub fn apply_additions(&mut self, additions: IndexedAdditions) { let IndexedAdditions { graph_additions, - index_delta, + index_additions, last_height, } = additions; - self.index.apply_additions(index_delta); + self.index.apply_additions(index_additions); for tx in &graph_additions.tx { self.index.index_tx(tx); @@ -122,16 +113,23 @@ impl IndexedTxGraph { } } - /// Insert a block height that the chain source has scanned up to. - pub fn insert_height(&mut self, tip: u32) -> IndexedAdditions { + fn insert_height_internal(&mut self, tip: u32) -> Option { if self.last_height < tip { self.last_height = tip; - IndexedAdditions { - last_height: Some(tip), - ..Default::default() - } + Some(tip) } else { - IndexedAdditions::default() + None + } + } + + /// Insert a block height that the chain source has scanned up to. + pub fn insert_height(&mut self, tip: u32) -> IndexedAdditions + where + I::Additions: Default, + { + IndexedAdditions { + last_height: self.insert_height_internal(tip), + ..Default::default() } } @@ -142,12 +140,12 @@ impl IndexedTxGraph { txout: &TxOut, observation: ObservedIn, ) -> IndexedAdditions { - let mut additions = match &observation { - ObservedIn::Block(anchor) => self.insert_height(anchor.anchor_block().height), - ObservedIn::Mempool(_) => IndexedAdditions::default(), + let last_height = match &observation { + ObservedIn::Block(anchor) => self.insert_height_internal(anchor.anchor_block().height), + ObservedIn::Mempool(_) => None, }; - additions.append_additions(IndexedAdditions { + IndexedAdditions { graph_additions: { let mut graph_additions = self.graph.insert_txout(outpoint, txout.clone()); graph_additions.append(match observation { @@ -158,11 +156,9 @@ impl IndexedTxGraph { }); graph_additions }, - index_delta: ::index_txout(&mut self.index, outpoint, txout), - last_height: None, - }); - - additions + index_additions: ::index_txout(&mut self.index, outpoint, txout), + last_height, + } } pub fn insert_tx( @@ -172,12 +168,12 @@ impl IndexedTxGraph { ) -> IndexedAdditions { let txid = tx.txid(); - let mut additions = match &observation { - ObservedIn::Block(anchor) => self.insert_height(anchor.anchor_block().height), - ObservedIn::Mempool(_) => IndexedAdditions::default(), + let last_height = match &observation { + ObservedIn::Block(anchor) => self.insert_height_internal(anchor.anchor_block().height), + ObservedIn::Mempool(_) => None, }; - additions.append_additions(IndexedAdditions { + IndexedAdditions { graph_additions: { let mut graph_additions = self.graph.insert_tx(tx.clone()); graph_additions.append(match observation { @@ -186,11 +182,9 @@ impl IndexedTxGraph { }); graph_additions }, - index_delta: ::index_tx(&mut self.index, tx), - last_height: None, - }); - - additions + index_additions: ::index_tx(&mut self.index, tx), + last_height, + } } pub fn filter_and_insert_txs<'t, T>( @@ -200,6 +194,7 @@ impl IndexedTxGraph { ) -> IndexedAdditions where T: Iterator, + I::Additions: Default + AddAssign, { txs.filter_map(|tx| { if self.index.is_tx_relevant(tx) { @@ -209,7 +204,7 @@ impl IndexedTxGraph { } }) .fold(IndexedAdditions::default(), |mut acc, other| { - acc.append_additions(other); + acc += other; acc }) } @@ -252,50 +247,47 @@ impl IndexedTxGraph { pub fn try_list_chain_txouts<'a, C>( &'a self, chain: C, - ) -> impl Iterator, C::Error>> + ) -> impl Iterator>, C::Error>> + 'a where C: ChainOracle + 'a, ObservedIn: ChainPosition, { - self.index.relevant_txouts().iter().filter_map( - move |(op, (spk_i, txout))| -> Option> { + self.graph + .all_txouts() + .filter(|(_, txo)| self.index.is_spk_owned(&txo.script_pubkey)) + .filter_map(move |(op, txout)| -> Option> { let graph_tx = self.graph.get_tx(op.txid)?; let is_on_coinbase = graph_tx.is_coin_base(); let chain_position = match self.graph.try_get_chain_position(&chain, op.txid) { - Ok(Some(observed_at)) => observed_at, + Ok(Some(observed_at)) => observed_at.into_owned(), Ok(None) => return None, Err(err) => return Some(Err(err)), }; - let spent_by = match self.graph.try_get_spend_in_chain(&chain, *op) { - Ok(spent_by) => spent_by, + let spent_by = match self.graph.try_get_spend_in_chain(&chain, op) { + Ok(Some((obs, txid))) => Some((obs.into_owned(), txid)), + Ok(None) => None, Err(err) => return Some(Err(err)), }; let full_txout = FullTxOut { - outpoint: *op, + outpoint: op, txout: txout.clone(), chain_position, spent_by, is_on_coinbase, }; - let txout_in_chain = TxOutInChain { - spk_index: spk_i, - txout: full_txout, - }; - - Some(Ok(txout_in_chain)) - }, - ) + Some(Ok(full_txout)) + }) } pub fn list_chain_txouts<'a, C>( &'a self, chain: C, - ) -> impl Iterator> + ) -> impl Iterator>> + 'a where C: ChainOracle + 'a, ObservedIn: ChainPosition, @@ -308,19 +300,19 @@ impl IndexedTxGraph { pub fn try_list_chain_utxos<'a, C>( &'a self, chain: C, - ) -> impl Iterator, C::Error>> + ) -> impl Iterator>, C::Error>> + 'a where C: ChainOracle + 'a, ObservedIn: ChainPosition, { self.try_list_chain_txouts(chain) - .filter(|r| !matches!(r, Ok(txo) if txo.txout.spent_by.is_none())) + .filter(|r| !matches!(r, Ok(txo) if txo.spent_by.is_none())) } pub fn list_chain_utxos<'a, C>( &'a self, chain: C, - ) -> impl Iterator> + ) -> impl Iterator>> + 'a where C: ChainOracle + 'a, ObservedIn: ChainPosition, @@ -338,7 +330,7 @@ impl IndexedTxGraph { where C: ChainOracle, ObservedIn: ChainPosition + Clone, - F: FnMut(&I::SpkIndex) -> bool, + F: FnMut(&Script) -> bool, { let mut immature = 0; let mut trusted_pending = 0; @@ -346,8 +338,7 @@ impl IndexedTxGraph { let mut confirmed = 0; for res in self.try_list_chain_txouts(&chain) { - let TxOutInChain { spk_index, txout } = res?; - let txout = txout.into_owned(); + let txout = res?; match &txout.chain_position { ObservedIn::Block(_) => { @@ -360,7 +351,7 @@ impl IndexedTxGraph { } } ObservedIn::Mempool(_) => { - if should_trust(spk_index) { + if should_trust(&txout.txout.script_pubkey) { trusted_pending += txout.txout.value; } else { untrusted_pending += txout.txout.value; @@ -381,7 +372,7 @@ impl IndexedTxGraph { where C: ChainOracle, ObservedIn: ChainPosition + Clone, - F: FnMut(&I::SpkIndex) -> bool, + F: FnMut(&Script) -> bool, { self.try_balance(chain, tip, should_trust) .expect("error is infallible") @@ -393,8 +384,8 @@ impl IndexedTxGraph { ObservedIn: ChainPosition + Clone, { let mut sum = 0; - for res in self.try_list_chain_txouts(chain) { - let txo = res?.txout.into_owned(); + for txo_res in self.try_list_chain_txouts(chain) { + let txo = txo_res?; if txo.is_spendable_at(height) { sum += txo.txout.value; } diff --git a/crates/chain/src/keychain.rs b/crates/chain/src/keychain.rs index da2af6f2..53da284f 100644 --- a/crates/chain/src/keychain.rs +++ b/crates/chain/src/keychain.rs @@ -14,12 +14,14 @@ //! [`KeychainChangeSet`]s. //! //! [`SpkTxOutIndex`]: crate::SpkTxOutIndex +use core::ops::AddAssign; + use crate::{ chain_graph::{self, ChainGraph}, collections::BTreeMap, sparse_chain::ChainPosition, tx_graph::TxGraph, - ForEachTxOut, TxIndexAdditions, + ForEachTxOut, }; #[cfg(feature = "miniscript")] @@ -85,9 +87,9 @@ impl DerivationAdditions { } } -impl TxIndexAdditions for DerivationAdditions { - fn append_additions(&mut self, other: Self) { - self.append(other) +impl AddAssign for DerivationAdditions { + fn add_assign(&mut self, rhs: Self) { + self.append(rhs) } } diff --git a/crates/chain/src/keychain/txout_index.rs b/crates/chain/src/keychain/txout_index.rs index d19aada7..101278b7 100644 --- a/crates/chain/src/keychain/txout_index.rs +++ b/crates/chain/src/keychain/txout_index.rs @@ -91,8 +91,6 @@ impl Deref for KeychainTxOutIndex { impl TxIndex for KeychainTxOutIndex { type Additions = DerivationAdditions; - type SpkIndex = (K, u32); - fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions { self.scan_txout(outpoint, txout) } @@ -109,8 +107,8 @@ impl TxIndex for KeychainTxOutIndex { self.is_relevant(tx) } - fn relevant_txouts(&self) -> &BTreeMap { - self.inner.relevant_txouts() + fn is_spk_owned(&self, spk: &Script) -> bool { + self.index_of_spk(spk).is_some() } } diff --git a/crates/chain/src/spk_txout_index.rs b/crates/chain/src/spk_txout_index.rs index 3d1af948..6c9739be 100644 --- a/crates/chain/src/spk_txout_index.rs +++ b/crates/chain/src/spk_txout_index.rs @@ -53,19 +53,16 @@ impl Default for SpkTxOutIndex { } impl TxIndex for SpkTxOutIndex { - type Additions = BTreeSet; - - type SpkIndex = I; + type Additions = (); fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions { - self.scan_txout(outpoint, txout) - .cloned() - .into_iter() - .collect() + self.scan_txout(outpoint, txout); + Default::default() } fn index_tx(&mut self, tx: &Transaction) -> Self::Additions { - self.scan(tx) + self.scan(tx); + Default::default() } fn apply_additions(&mut self, _additions: Self::Additions) { @@ -76,8 +73,8 @@ impl TxIndex for SpkTxOutIndex { self.is_relevant(tx) } - fn relevant_txouts(&self) -> &BTreeMap { - &self.txouts + fn is_spk_owned(&self, spk: &Script) -> bool { + self.index_of_spk(spk).is_some() } } diff --git a/crates/chain/src/tx_data_traits.rs b/crates/chain/src/tx_data_traits.rs index 485e3f70..0e2474c4 100644 --- a/crates/chain/src/tx_data_traits.rs +++ b/crates/chain/src/tx_data_traits.rs @@ -1,5 +1,4 @@ -use alloc::collections::{BTreeMap, BTreeSet}; -use bitcoin::{Block, BlockHash, OutPoint, Transaction, TxOut}; +use bitcoin::{Block, BlockHash, OutPoint, Script, Transaction, TxOut}; use crate::BlockId; @@ -89,41 +88,16 @@ impl ChainOracle for &C { } } -/// Represents changes to a [`TxIndex`] implementation. -pub trait TxIndexAdditions: Default { - /// Append `other` on top of `self`. - fn append_additions(&mut self, other: Self); -} - -impl TxIndexAdditions for BTreeSet { - fn append_additions(&mut self, mut other: Self) { - self.append(&mut other); - } -} - /// Represents an index of transaction data. pub trait TxIndex { /// The resultant "additions" when new transaction data is indexed. - type Additions: TxIndexAdditions; - - type SpkIndex: Ord; + type Additions; /// Scan and index the given `outpoint` and `txout`. fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions; /// Scan and index the given transaction. - fn index_tx(&mut self, tx: &Transaction) -> Self::Additions { - let txid = tx.txid(); - tx.output - .iter() - .enumerate() - .map(|(vout, txout)| self.index_txout(OutPoint::new(txid, vout as _), txout)) - .reduce(|mut acc, other| { - acc.append_additions(other); - acc - }) - .unwrap_or_default() - } + fn index_tx(&mut self, tx: &Transaction) -> Self::Additions; /// Apply additions to itself. fn apply_additions(&mut self, additions: Self::Additions); @@ -132,6 +106,6 @@ pub trait TxIndex { /// spends an already-indexed outpoint that we have previously indexed. fn is_tx_relevant(&self, tx: &Transaction) -> bool; - /// Lists all relevant txouts known by the index. - fn relevant_txouts(&self) -> &BTreeMap; + /// Returns whether the script pubkey is owned by us. + fn is_spk_owned(&self, spk: &Script) -> bool; }