@@ -5,11 +5,9 @@ use core::ops::RangeBounds;
55
66use crate :: {
77 collections:: { hash_map:: Entry , BTreeMap , BTreeSet , HashMap } ,
8- IndexedTxOuts , Indexer ,
8+ Indexer ,
99} ;
10- use bitcoin:: { Amount , OutPoint , Script , ScriptBuf , SignedAmount , Transaction , TxOut , Txid } ;
11-
12- use alloc:: vec:: Vec ;
10+ use bitcoin:: { Amount , OutPoint , Script , ScriptBuf , SignedAmount , Transaction , TxIn , TxOut , Txid } ;
1311
1412/// An index storing [`TxOut`]s that have a script pubkey that matches those in a list.
1513///
@@ -320,17 +318,14 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
320318 ( sent, received)
321319 }
322320
323- /// Collects the sent and received [`TxOut`]s for `tx` on the script pubkeys in `range`.
324- /// TxOuts are *sent* when a script pubkey in the `range` is on an input and *received* when
325- /// it is on an output. For `sent` to be computed correctly, the index must have already
326- /// scanned the output being spent. Calculating received just uses the [`Transaction`]
327- /// outputs directly, so it will be correct even if it has not been scanned.
321+ /// Returns the relevant [`SpentTxOut`]s for a [`Transaction`]
328322 ///
329- /// Returns a tuple of (sent_txouts, received_txouts).
323+ /// TxOuts are *spent* when an indexed script pubkey is found in one of the transaction's
324+ /// inputs. For these to be computed correctly, the index must have already scanned the
325+ /// output being spent.
330326 ///
331327 /// # Example
332- /// Shows the addresses of the TxOut sent from or received by a Transaction relevant to all spks
333- /// in this index.
328+ /// Shows the addresses of the TxOut spent from a Transaction relevant to spks in this index.
334329 ///
335330 /// ```rust
336331 /// # use bdk_chain::spk_txout::SpkTxOutIndex;
@@ -343,49 +338,86 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
343338 /// // ... scan transactions to populate the index ...
344339 /// # let tx = Transaction { version: bitcoin::transaction::Version::TWO, lock_time: bitcoin::locktime::absolute::LockTime::ZERO, input: vec![], output: vec![] };
345340 ///
346- /// // Get sent and received txouts for a transaction across all tracked addresses
347- /// let (sent_txouts, received_txouts) = index.sent_and_received_txouts (&tx, .. );
341+ /// // Get spent txouts for a transaction for all indexed spks
342+ /// let spent_txouts = index.spent_txouts (&tx);
348343 ///
349344 /// // Display addresses and amounts
350- /// println!("Sent :");
351- /// for (i, txout) in sent_txouts {
352- /// let address = Address::from_script(&txout.script_pubkey, Network::Bitcoin)?;
353- /// println!("input {}: from {} - {} sats ", i , address, txout.value.to_sat());
345+ /// println!("Spent :");
346+ /// for spent in spent_txouts {
347+ /// let address = Address::from_script(&spent. txout.script_pubkey, Network::Bitcoin)?;
348+ /// println!("input {}: from {} - {}", spent.outpoint().vout , address, &spent. txout.value.to_sat());
354349 /// }
350+ /// # Ok(())
351+ /// # }
352+ /// ```
353+ pub fn spent_txouts < ' a > (
354+ & ' a self ,
355+ tx : & ' a Transaction ,
356+ ) -> impl Iterator < Item = SpentTxOut < I > > + ' a {
357+ tx. input
358+ . iter ( )
359+ . enumerate ( )
360+ . filter_map ( |( input_index, txin) | {
361+ self . txout ( txin. previous_output )
362+ . map ( |( index, txout) | SpentTxOut {
363+ txout : txout. clone ( ) ,
364+ spending_input : txin. clone ( ) ,
365+ spending_input_index : u32:: try_from ( input_index)
366+ . expect ( "invalid input index" ) ,
367+ spk_index : index. clone ( ) ,
368+ } )
369+ } )
370+ }
371+
372+ /// Returns the relevant [`CreatedTxOut`]s for a [`Transaction`]
355373 ///
356- /// println!("Received:");
357- /// for (i, txout) in received_txouts {
358- /// let address = Address::from_script(&txout.script_pubkey, Network::Bitcoin)?;
359- /// println!("output {}: to {} + {} sats", i, address, txout.value.to_sat());
374+ /// TxOuts are *created* when an indexed script pubkey is found in one of the transaction's
375+ /// outputs. These are computed directly from the transaction outputs.
376+ ///
377+ /// # Example
378+ /// Shows the addresses of the TxOut created by a Transaction relevant to spks in this index.
379+ ///
380+ /// ```rust
381+ /// # use bdk_chain::spk_txout::SpkTxOutIndex;
382+ /// # use bitcoin::{Address, Network, Transaction};
383+ /// # use std::str::FromStr;
384+ /// #
385+ /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
386+ /// let mut index = SpkTxOutIndex::<u32>::default();
387+ ///
388+ /// // ... scan transactions to populate the index ...
389+ /// # let tx = Transaction { version: bitcoin::transaction::Version::TWO, lock_time: bitcoin::locktime::absolute::LockTime::ZERO, input: vec![], output: vec![] };
390+ ///
391+ /// // Get created txouts for a transaction for all indexed spks
392+ /// let created_txouts = index.created_txouts(&tx);
393+ ///
394+ /// // Display addresses and amounts
395+ /// println!("Created:");
396+ /// for created in created_txouts {
397+ /// let address = Address::from_script(&created.txout.script_pubkey, Network::Bitcoin)?;
398+ /// println!("output {}: to {} + {}", &created.outpoint.vout, address, &created.txout.value.display_dynamic());
360399 /// }
361400 /// # Ok(())
362401 /// # }
363402 /// ```
364- pub fn sent_and_received_txouts (
365- & self ,
366- tx : & Transaction ,
367- range : impl RangeBounds < I > ,
368- ) -> ( IndexedTxOuts , IndexedTxOuts ) {
369- let mut sent = Vec :: new ( ) ;
370- let mut received = Vec :: new ( ) ;
371-
372- for ( i, txin) in tx. input . iter ( ) . enumerate ( ) {
373- if let Some ( ( index, txout) ) = self . txout ( txin. previous_output ) {
374- if range. contains ( index) {
375- sent. push ( ( i, txout. clone ( ) ) ) ;
376- }
377- }
378- }
379-
380- for ( i, txout) in tx. output . iter ( ) . enumerate ( ) {
381- if let Some ( index) = self . index_of_spk ( txout. script_pubkey . clone ( ) ) {
382- if range. contains ( index) {
383- received. push ( ( i, txout. clone ( ) ) ) ;
384- }
385- }
386- }
387-
388- ( sent, received)
403+ pub fn created_txouts < ' a > (
404+ & ' a self ,
405+ tx : & ' a Transaction ,
406+ ) -> impl Iterator < Item = CreatedTxOut < I > > + ' a {
407+ tx. output
408+ . iter ( )
409+ . enumerate ( )
410+ . filter_map ( |( output_index, txout) | {
411+ self . index_of_spk ( txout. script_pubkey . clone ( ) )
412+ . map ( |index| CreatedTxOut {
413+ outpoint : OutPoint {
414+ txid : tx. compute_txid ( ) ,
415+ vout : u32:: try_from ( output_index) . expect ( "invalid output index" ) ,
416+ } ,
417+ txout : txout. clone ( ) ,
418+ spk_index : index. clone ( ) ,
419+ } )
420+ } )
389421 }
390422
391423 /// Computes the net value transfer effect of `tx` on the script pubkeys in `range`. Shorthand
@@ -437,3 +469,38 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
437469 spks_from_inputs. chain ( spks_from_outputs) . collect ( )
438470 }
439471}
472+
473+ /// A transaction output that was spent by a transaction input.
474+ ///
475+ /// Contains information about the spent output and the input that spent it.
476+ #[ derive( Clone , Debug , Eq , Hash , PartialEq , PartialOrd , Ord ) ]
477+ pub struct SpentTxOut < I > {
478+ /// The transaction output that was spent.
479+ pub txout : TxOut ,
480+ /// The transaction input that spent the output.
481+ pub spending_input : TxIn ,
482+ /// The index of the spending input in the transaction.
483+ pub spending_input_index : u32 ,
484+ /// The script pubkey index associated with the spent output.
485+ pub spk_index : I ,
486+ }
487+
488+ impl < I > SpentTxOut < I > {
489+ /// Returns the outpoint of the spent transaction output.
490+ pub fn outpoint ( & self ) -> OutPoint {
491+ self . spending_input . previous_output
492+ }
493+ }
494+
495+ /// A transaction output that was created by a transaction.
496+ ///
497+ /// Contains information about the created output and its location.
498+ #[ derive( Clone , Debug , Eq , Hash , PartialEq , PartialOrd , Ord ) ]
499+ pub struct CreatedTxOut < I > {
500+ /// The outpoint identifying the created output.
501+ pub outpoint : OutPoint ,
502+ /// The transaction output that was created.
503+ pub txout : TxOut ,
504+ /// The script pubkey index associated with the created output.
505+ pub spk_index : I ,
506+ }
0 commit comments